Rspec: "array.should == another_array" but without concern for order

RubyTestingRspec

Ruby Problem Overview


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 #<=>

Ruby Solutions


Solution 1 - Ruby

Try array.should =~ another_array

The best documentation on this I can find is the code itself, which is here.

Solution 2 - Ruby

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])

Solution 3 - Ruby

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])

Solution 4 - Ruby

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

Solution 5 - Ruby

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

Solution 6 - Ruby

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])

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionnicholaidesView Question on Stackoverflow
Solution 1 - Rubyx1a4View Answer on Stackoverflow
Solution 2 - RubyValentin NemcevView Answer on Stackoverflow
Solution 3 - RubyJosh KovachView Answer on Stackoverflow
Solution 4 - RubynicholaidesView Answer on Stackoverflow
Solution 5 - RubyTim DigginsView Answer on Stackoverflow
Solution 6 - RubyBlair AndersonView Answer on Stackoverflow