3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RSpecの `contain_exactly` 相当でハッシュを検証する方法に悩んでしまった話

Posted at

ハッシュの { 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
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?