岩本隆史の日記帳(アーカイブ)

はてなダイアリーのサービス終了をうけて移行したものです。更新はしません。

どうすればCucumberで例外をテストできるのか

なぜCucumberを選んだのか

BDDでユーザ登録処理を実装し始めた。テスティングフレームワークにはCucumberを使っている。

Cucumberを選んだのは、前提条件(Given)を明示的に書けるのが気に入ったからだ。以前使っていたRSpecでは書けなかった(RSpec::Storyを使えば書けたのかもしれないが、その存在を私は知らなかった)。

多くの場合、ある処理の結果が一定になるためには、前提条件の指定が必要なはずだ。たとえばユーザ登録処理について考えれば、正常登録ケースのテストにおいては「同じユーザIDのユーザが未登録であること」という前提条件の指定が必要になる。もし登録済みであれば、重複エラーとなるのが望ましい結果だろう。

Cucumberならば、前提条件(Given)・処理(When)・期待される結果(Then)が書ける。このようなシナリオベースのテスティングフレームワークでなければ、私はもうテストを書く自信がない。

どうすればCucumberで例外をテストできるのか

Cucumberを使っていて悩んだのが、異常ケースのテスト方法だ。たとえば、DBが止まっているときにユーザ登録処理を走らせたら、例外が発生すべきだろう。だとすれば、以下のようなシナリオが書きたい。

Given: DBが止まっている
When: ユーザ登録処理を実行した
Then: RuntimeErrorが発生する

ここまではよいのだが、悩んだのは、例外の発生をどうやってassertするかだ。Thenステップ内で例外をassertしようとすると、下記のように書かざるをえない。

Then /^RuntimeErrorが発生する$/ do
  lambda { do_registration(@id, @pass) }.should raise_error(RuntimeError)
end

しかしこれでは、Whenステップ内に書かれるべきユーザ登録処理が、Thenステップに書かれることになってしまう。

海外のQ&Aサイト・Stack Overflowでは、Whenステップ内でassertする方法が紹介されている。が、これも私には気持ちが悪い。Whenステップにはテスト対象の処理しか書きたくないからだ。

しばし考えて思いついたのは、以下のように、テスト対象の処理をbegin式でくるむ方法だ。

When /^ユーザ登録処理を実行した$/ do
  begin
    @result = do_registration(@id, @pass)
  rescue => @exception
  end
end

Then /^RuntimeErrorが発生する$/ do
  @exception.should be_a_kind_of(RuntimeError)
end

今のところ、これでよいのではないかと思っている。正常ケースのテストにおいては、戻り値のassertをするだろうから、想定外の例外が発生したらredになるはずだ。

Cucumberで例外のテストをすべきか

別の論点として、そもそもCucumberで例外のテストをすべきかどうかという話もあろう。私は、Cucumberが受け入れテスト向けだからといって、こんな素晴らしいテスティングフレームワークを内部処理のテストに使わないのはもったいないことだと思っている。