Thanks Driven Life

日々是感謝

RSpec 3 から RSpec::Expectations::ExpectationNotMetError がクラス名省略の rescue で捕まえられなくなった

結論

rescue RSpec::Expectations::ExpectationNotMetError => e

明示的 に書けば大丈夫です。もし

rescue => e

とかしている場合は rescue に引っ掛からない、というところでハマりました。

動機

  1. TurnipFormatter の RSpec 3.0 対応 をしていた
  2. Turnip は Failed な Example が持つ exception backtrace の末尾に、失敗したシナリオの情報を追記する
  3. TurnipFormatter はその情報を使ってほげほげいているんだけど、なんか RSpec (正確には rspec-expectations) が 3 系になってから ↑ の rescue StandardError => e に入らなくなっていた

原因

rspec-expectations が持つマッチャ(よくある expect(1).to be 1 みたいなやつ)が、失敗時には ExpectationNotMetError を raise していて、これは RSpec 2 および 3 系どちらもで同じ挙動です。

じゃあなんで 3 系でだけ rescue に入ってこなかったかというと ExpectationNotMetError の定義によるものでした。

RSpec 2.14.5

class ExpectationNotMetError < ::StandardError; end

see v2.14.5/lib/rspec/expectations/errors.rb#L4-L6

RSpec 3.0.3

ExpectationNotMetError = Class.new(::Exception)

see v3.0.3/lib/rspec/expectations.rb#L66)

つまり

  1. rescue 時にクラス名を省略すると StandardError => e と同義になる (たぶん)
  2. RSpec 3 系からは ExpectationNotMetErrorException のサブクラス ? になった

というわけでした

まとめ

RSpecプラグイン系を書いてないと ExpectationNotMetError を捕まえようとは思わないはずなのであまり気にならない仕様変更かなーとは思うけど、ちょっと最初意味わからんかった。