ハッシュの { a: 1, b: 2 }
と { b: 2, a: 1 }
が、順序を無視して完全に一致するかテストしたかった。
contain_exactly
RSpecには順序を無視してくれる contain_exactly
があるが、これは配列に対して使うものらしい。
https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/contain-exactly-matcher
The contain_exactly matcher provides a way to test arrays against each other in a way that disregards differences in the ordering between the actual and expected array.
ハッシュでやってみると実際おかしなことになって失敗する。
Failure/Error: expect({ a: 1, b: 2 }).to contain_exactly({ b: 2, a: 1 })
expected collection contained: [{:a=>1, :b=>2}]
actual collection contained: [[:a, 1], [:b, 2]]
the missing elements were: [{:a=>1, :b=>2}]
the extra elements were: [[:a, 1], [:b, 2]]
エラーメッセージから考えると、一応 contain_exactly
側のハッシュを *
で展開すればテストが通る。
require 'rspec/core'
RSpec.describe 'Hash' do
it 'contains exactly' do
expect({ a: 1, b: 2 }).to contain_exactly(*{ b: 2, a: 1 })
end
end
しかしもっと単純な方法があった。
結論
普通に eq
で検証すればいい。
require 'rspec/core'
RSpec.describe 'Hash' do
it 'contains exactly' do
expect({ a: 1, b: 2 }).to eq({ b: 2, a: 1 })
end
end
内容が異なる場合も、エラーメッセージで差分を出してくれるのでわかりやすい。
Failure/Error: expect({ a: 1, b: 2 }).to eq({ b: 2, a: 3 })
expected: {:a=>3, :b=>2}
got: {:a=>1, :b=>2}
(compared using ==)
Diff:
@@ -1,3 +1,3 @@
-:a => 3,
+:a => 1,
:b => 2,
理由
参考:https://docs.ruby-lang.org/ja/latest/class/Hash.html
リファレンスマニュアルに明記されてはいないが、ハッシュは ==
で比較するときに要素の順序は考慮しない。
自身と other が同じ数のキーを保持し、キーが eql? メソッドで比較して全て等しく、 値が == メソッドで比較して全て等しい場合に真を返します。
{ a: 1, b: 2 } == { b: 2, a: 1 } #=> true
なので、 ==
で比較を行う eq
を使えばいい。
ただし、ハッシュは要素の順序を保持しているので、配列に変換したりループを回したりすると違いが現れる。
ハッシュに含まれる要素の順序が保持されるようになりました。ハッシュにキーが追加された順序で列挙します。
{ a: 1, b: 2 }.to_a == { b: 2, a: 1 }.to_a #=> false