96
62

More than 5 years have passed since last update.

RSpec でエラーを捉えらんないアレなミス

Last updated at Posted at 2016-06-09

考えてみれば当たり前の話。

でも RSpec から離れるとまたやらかしそうなので、メモ。

環境

  • RSpec 3.4.4
  • Ruby 2.2.3

参考

捉えらんない

expect(instance.method).to raise_error(MyError)

上記では instance.method の処理で起きたエラーを捉えらんない。処理は expect メソッドに渡される前に実行されるからだ。

当たり前。

しかし expect といえば検査対象を括弧に入れるものだと思い込んでいた。

捉える

エラーを捉えるには、エラーが発生する処理を expect のブロックに入れる。

expect { instance.method }.to raise_error(MyError)

捉えらんない 2

is_expected を使ったときにも遭遇。

subject { instance.method }
is_expected.to raise_error(MyError)

subject 内でエラーを起こしても is_expected では捉えらんない。これは捉えられてもいいんじゃないか感。

捉える

愚痴っていたら、コメント欄にて @jnchito さんからアドバイスを頂いた。:thumbsup:

 is_expectedを使うときはラムダやProcを使うと、期待した動きになりますよ。

describe 'raise' do
  subject { -> { raise 'test' } }
  # ProcでもOK
  # subject { Proc.new { raise 'test' } }
  it { is_expected.to raise_error(RuntimeError, 'test') }
end

アドバイスを元にコードを試したり、あらためて仕様を読んだりする。

is_expected の検査対象は subject の状態で決まる。これは subject {} のようにブロックを渡すことで更新できるのだけど、保持・再利用されるのはブロックそのものではなく、このブロックの返り値。

subject が保持する値が Proc や lambda の場合 is_expectedexpect {} と同等となり、それ以外の値では expect() と同等となる。

問題となったコード例を書き換えるとこうなる。

subject { -> { instance.method } }
is_expected.to raise_error(MyError)

こうでもいける。

subject { instance.method }
expect { subject }.to raise_error(MyError)

後者の方は、例えば引数によってインスタンスの生成に失敗することをテストしたい場合等に subject を使いまわせる利点がある。

感謝!

96
62
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
96
62