考えてみれば当たり前の話。
でも 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 を使いまわせる利点がある。
感謝!