Search code examples
ruby-on-railsrubyrspec

Testing the contents of an array inside a hash


In Rspec I am testing a hash that contains an array like this:

subject { described_class.call(build: build_one, user:)

it 'returns the correct data' do
  response = subject

  expected_response = {
    id: build_one.id,
    status: {
      key: 'active',
      name: 'Active'
    },
    build_type: 'SystemBuild',
    calculated_type_id: CalculatedType.standard.id,
    build_time: build_one.build_time,
    summary: 'Lorem ipsum dolor sit amet...',
    data: [
      {id: '1', name: '', parent: ''},
      {id: '1.1', name: 'Calc type 1', parent: '1'},
      {id: '1.2', name: 'Calc type 2', parent: '1'},
      {id: '1.3', name: 'Calc type 3', parent: '1'},
      {id: '1.1.1', name: 'Value 1', parent: '1.1'},
      {id: '1.1.2', name: 'Value 2', parent: '1.1'},
      {id: '1.2.1', name: 'Value 3', parent: '1.2'},
      {id: '1.3.1', name: 'Value 4', parent: '1.3'},
      {id: '1.3.2', name: 'Value 5', parent: '1.3'},
      {id: '1.3.3', name: 'Value 6', parent: '1.3'}
    ]
  }

  expect(response).to match(expected_response)
end

My problem is that the data array is not necessarily in the same order as I have written in the expected_response.

I could of course changed the order in the expected_response, but I don't want to restrict it in this way. The thing is for the application, the array order does not matter at all, and changing other parts of the application might change the order. The only important thing is that all elements are present in the array.

When I run the test, I get the following:

Diff:

 :build_time: 2024-06-22 10:17:55.0000000000 -0700,
 :build_type: "SystemType",
 :calculated_type_id: 3,
 :id => 234,
-:data => [{:id=>"1", :name=>"", :parent=>""}, {:id=>"1.1", :name=>"Calc type 1", :parent=>"1"}, {:id=>"1.2", :name=>"Calc type 2", :parent=>"1"}, {:id=>"1.3", :name=>"Calc type 3", :parent=>"1"}, {:id=>"1.1.1", :name=>"Value 1", :parent=>"1.1"}, {:id=>"1.1.2", :name=>"Value 2", :parent=>"1.1"}, {:id=>"1.2.1", :name=>"Value 3", :parent=>"1.2"}, {:id=>"1.3.1", :name=>"Value 4", :parent=>"1.3"}, {:id=>"1.3.2", :name=>"Value 5", :parent=>"1.3"}, {:id=>"1.3.3", :name=>"Value 6", :parent=>"1.3"}]
+:data => [{:id=>"1", :name=>"", :parent=>""}, {:id=>"1.1", :name=>"Calc type 1", :parent=>"1"}, {:id=>"1.2", :name=>"Calc type 2", :parent=>"1"}, {:id=>"1.3", :name=>"Calc type 3", :parent=>"1"}, {:id=>"1.1.1", :name=>"Value 1", :parent=>"1.1"}, {:id=>"1.2.1", :name=>"Value 3", :parent=>"1.2"}, {:id=>"1.3.2", :name=>"Value 5", :parent=>"1.3"}, {:id=>"1.1.2", :name=>"Value 2", :parent=>"1.1"}, {:id=>"1.3.1", :name=>"Value 4", :parent=>"1.3"}, {:id=>"1.3.3", :name=>"Value 6", :parent=>"1.3"}]
 :status => {:key=>"active", :name=>"Active"},
 :summary => "Lorem ipsum dolor sit amet..."

I thought that match(...) would disregard the order of the data array and just check that all elements exists. Is there a way to do this without having to check each element in the hash?


Solution

  • When using match with a data structure (array or hash), you can utilize other RSpec matchers within that structure, e.g. contain_exactly:

    let(:expected) do
      {
        data: contain_exactly(1, 2, 3)
      }
    end
    
    it 'matches' do
      expect({ data: [2, 3, 1] }).to match(expected)
    end