I often want to compare arrays and make sure that they contain the same elements, in any order. Is there a concise way to do this in RSpec?
Here are methods that aren't acceptable:
#to_set
For example:
expect(array.to_set).to eq another_array.to_set
or
array.to_set.should == another_array.to_set
This fails when the arrays contain duplicate items.
#sort
For example:
expect(array.sort).to eq another_array.sort
or
array.sort.should == another_array.sort
This fails when the arrays elements don't implement #<=>
to_set
and size
actually doesn't do what you want. E.g. [a, b, b] would match [a, a, b]. Cheers!
eq
matcher, e.g. expect([1, 2]).to_not eq([2, 1])
Try array.should =~ another_array
The best documentation on this I can find is the code itself, which is here.
Since RSpec 2.11 you can also use match_array
.
array.should match_array(another_array)
Which could be more readable in some cases.
[1, 2, 3].should =~ [2, 3, 1]
# vs
[1, 2, 3].should match_array([2, 3, 1])
eq()
, so I guess match_array()
is vague enough for me.
I've found =~
to be unpredictable and it has failed for no apparent reason. Past 2.14, you should probably use
expect([1, 2, 3]).to match_array([2, 3, 1])
expect([1, 2, 3]).to contain_exactly(2, 3, 1)
. relishapp.com/rspec/rspec-expectations/v/3-0/docs/…
Use match_array
, which takes another array as an argument, or contain_exactly
, which takes each element as a separate argument, and is sometimes useful for readability. (docs)
Examples:
expect([1, 2, 3]).to match_array [3, 2, 1]
or
expect([1, 2, 3]).to contain_exactly 3, 2, 1
For RSpec 3 use contain_exactly
:
See https://relishapp.com/rspec/rspec-expectations/v/3-2/docs/built-in-matchers/contain-exactly-matcher for details, but here's an extract:
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. For example: expect([1, 2, 3]).to contain_exactly(2, 3, 1) # pass expect([:a, :c, :b]).to contain_exactly(:a, :c ) # fail
As others have pointed out, if you want to assert the opposite, that the arrays should match both contents and order, then use eq
, ie.:
expect([1, 2, 3]).to eq([1, 2, 3]) # pass
expect([1, 2, 3]).to eq([2, 3, 1]) # fail
not documented very well but i added links anyways:
Rspec3 docs
expect(actual).to eq(expected)
Rspec2 docs
expect([1, 2, 3]).to match_array([2, 3, 1])
.to eq
method, not the match_array
.
Success story sharing
Passes if actual contains all of the expected regardless of order.
.expect([1, 2, 3]).to match_array([2, 1, 3])
see: stackoverflow.com/a/19436763/33226