RSpec 3.8.0 で Change マッチャーの細かな振る舞いが変わった

RSpec 3.8.0 で RSpec::Matchers::BuiltIn::Change マッチャーによる判定に Object#hash も使われるようになった。

以下の PR での変更となる。

github.com

ピンポイントで示すと以下の Change#changed? メソッドの実装で、変更前に保持している hash 値と変更後の hash 値での判定が加わっている (ソースコードコメントにも記されているとおり) 。

github.com

以下、ニッチなケースの話。

Change マッチャーを使ったテストコード例として、以下の model がテスト対象の自前のクラスのオブジェクトだとする。そしてそれが eql? メソッドだけオーバーライドして、hash メソッドをオーバーライドし忘れているようなケースだと、上記 PR の変更によって期待通りのテスト結果を得ることができなくなる。

expect {
  model.do_something
}.not_to change { model }

RSpec 3.7.0 から RSpec 3.8.0 にアップグレードして上記のようなコードでの結果が思わしくなかった場合は、model のクラスの hash メソッドについてオーバーライドもれがないか見てみると良い。

るりまにも示されているように hash メソッドをオーバーライドする必要があるケースをきちんと押さえておくと良いと思いながら、もしかすると Lint として Cop を用意すると良いかもというアイデアにもなったので備忘を兼ねてメモっておく。

docs.ruby-lang.org

当初 RSpec のバグかと思ってみたら意図のある変更で、使っている側の問題だったという。想定される利用規模に対してレポートもないのでそれもそうだったかという感じではあった。