Home > Mobile >  How to compare hash containing array but ignoring order inside the array
How to compare hash containing array but ignoring order inside the array

Time:09-16

In my spec i have the following response structure

something: { foo: [{bar: 'baz'}, {one: 'two'}] }

I try to compare it but the order inside of foo-array is random. I've found this article: link, and in it, there is the following code:

Use a_collection_containing_exactly when you have an array, but can’t determine the order of elements

expected = {
  "data" => a_collection_containing_exactly(
    a_hash_including("id" => "1"),
    a_hash_including("id" => "2")
  )
}
expect(response.parsed_body).to include(expected)

But it does not work for me as the comparison is seeing matcher a_collection_containing_exactly as part of the hash like

{"base_matcher"=>{"expected"=>[{bar: 'baz'}, {one: 'two'}]}}

What is that I have missed? Is there any better solution?

Edit: To clarify, here is a minimal reproducible example

expected_value = { something: { foo: [{ bar: 'baz' }, { one: 'two' }] }}

expect(response.parsed_body).to eq(expected_value)

CodePudding user response:

I think the RSpec match_array matcher is what you need:

array_of_hashes = [{bar: "baz"},{one: "two"}]

expect(response.parsed_body[:foo]).to match_array(array_of_hashes)

if your top-level hash has many keys, you can write a custom matcher:

# in spec/matchers/match_sort_indifferent.rb:

require 'rspec/expectations'

RSpec::Matchers.define :match_sort_indifferent do |expected|
  match do |actual|
    expected.keys.each do |key|
      expect(actual[key]).to match_array expected[key]
    end
    expect(expected.keys.length).to eq actual.keys.length
  end
end


# in spec/models/test_spec.rb

require 'rails_helper'
require 'matchers/match_sort_indifferent'

result = {foo: [{bar: "baz"},{qux: "dak"}]}

describe {
  it {
    expect(result).to match_sort_indifferent({foo: [{qux: "dak"},{bar: "baz"}]})
    expect(result).not_to match_sort_indifferent({foo: [{qux: "sum"},{bar: "baz"}]})
  }
}

CodePudding user response:

Thanks for the answers. I solved the problem with a help of Rspec-json-expectations gem and UnorderedArray

So my solution looks like this:

expected_value = { something: { foo: UnorderedArray({ bar: 'baz' }, { one: 'two' }) }}

and the matcher

expect(response.parsed_body).to include_json expected_value

The downside is that it only checks if the expected values are present, and doesn't catch the extra ones.

  • Related