RSpec の contain_exactly マッチャ と match_array マッチャ

contain_exactly マッチャ

`contain_exactly` matcher - Built in matchers - RSpec Expectations - RSpec - Relish

以下のようにコレクションに対して、順序は問わないがすべてが含まれているかをチェックすることができる。

expect([1, 2, 3]).to contain_exactly(2, 3, 1) # pass
expect([:a, :c, :b]).to contain_exactly(:a, :c) # fail

match_array マッチャ

ほぼ同じようなマッチャとして match_array というのもある。 これは引数に配列を取るので注意する。

expect([1, 2, 3]).to match_array [2, 3, 1] # pass
expect([:a, :c, :b]).to match_array [:a, :c]  # fail

ソースコードを読む

これらのマッチャの動作を理解するために、まずは contain_exactlyソースコードを読んでみた。

rspec-expectations/contain_exactly.rb at 99f9bcaff2a6f3d82f4e350e829eca6ab015694f · rspec/rspec-expectations · GitHub

def match(_expected, _actual)
  return false unless convert_actual_to_an_array
  match_when_sorted? || (extra_items.empty? && missing_items.empty?)
end

def match_when_sorted?
  values_match?(safe_sort(expected), safe_sort(actual))
end

引数を配列に変換して、ソートしたあとで、それぞれマッチするかをチェックしている。

一方 match_array は以下のような感じ。

https://github.com/rspec/rspec-expectations/blob/99f9bcaff2a6f3d82f4e350e829eca6ab015694f/lib/rspec/matchers.rb#L715

def match_array(items)
  contain_exactly(*items)
end

contain_exactly 呼んでた。 ということで contain_exactlymatch_array は引数に配列を取るかどうか以外は同じ挙動をすることがわかった。

まとめ

RSpec は奥が深い。