考えてみれば当たり前の話。
でも RSpec から離れるとまたやらかしそうなので、メモ。
環境
- RSpec 3.4.4
- Ruby 2.2.3
参考
-
Module: RSpec::Matchers — Documentation by YARD 0.8.7.4
-
raise_error
の項目
-
-
Module: RSpec::Core::MemoizedHelpers — Documentation by YARD 0.8.7.6
-
subject
の項目
-
捉えらんない
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 さんからアドバイスを頂いた。
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_expected
は expect {}
と同等となり、それ以外の値では expect()
と同等となる。
問題となったコード例を書き換えるとこうなる。
subject { -> { instance.method } }
is_expected.to raise_error(MyError)
こうでもいける。
subject { instance.method }
expect { subject }.to raise_error(MyError)
後者の方は、例えば引数によってインスタンスの生成に失敗することをテストしたい場合等に subject
を使いまわせる利点がある。
感謝!