0

I need to verify the contents of a hash, and I was surprised to find that RSpec's contain_exactly only works on arrays. The ideal expectation is:

expect(type.values.values).to contain_exactly(
  ONE: an_object_having_attributes(value: 'uno'),
  TWO: an_object_having_attributes(value: 'dos')
)

The base requirement is that contain_exactly requires that the array have only those elements, and a hash equivalent must only contain the exact key/value pairs specified.

There's plenty of workarounds that are just okay:

  • include(key: value), but this allows other keys, I need an exact match.
  • expect(hash.keys).to contain_exactly(...) but that doesn't verify that the keys are specifically linked to the values.
  • use contain_exactly anyway (which reads the hash as a tuple of [key, value]) and match based on the sub-array e.g. contain_exactly(a_collection_containing_exactly('ONE', an_object_having_attributes(value: 'uno')), ...)
  • iterate through the hash and match keys to values based on an expected input with aggregate_failures.

etc. but I'm mostly curious if there's a built-in RSpec way to do this.

mechnicov
  • 12,025
  • 4
  • 33
  • 56
brainbag
  • 1,007
  • 9
  • 23

1 Answers1

7

You can use match matcher like this

require "ostruct"

describe do
  let(:hash) do
    {
      one: OpenStruct.new(x: 1),
      two: OpenStruct.new(y: 2)
    }
  end

  it "matches hashes" do
    expect(hash).to match(
      two: an_object_having_attributes(y: 2),
      one: an_object_having_attributes(x: 1)
    )
  end

  it "requires the actual to have all elements of the expected" do
    expect(a: 1, b: 2).not_to match(b: 2)
  end

  it "requires the expected to have all elements of the actual" do
    expect(a: 1).not_to match(a: 1, b: 2)
  end
end

If one of this hashes has extra key(s) -- test will fail

Jordan Brough
  • 6,808
  • 3
  • 31
  • 31
mechnicov
  • 12,025
  • 4
  • 33
  • 56
  • 1
    This does not satisfy the requirement that it must match exactly the inputs. This will still succeed if `hash` has other keys. This is implied by the use case of `contain_exactly`, but I added an extra line to clarify. – brainbag Mar 15 '22 at 15:52
  • What inputs? This code check if hash contains only given keys as you write in your question, also check values like in your question – mechnicov Mar 15 '22 at 16:17
  • 1
    I believe this answer is correct. I added some more examples to the answer and they all pass for me with RSpec 3.12.0. – Jordan Brough May 25 '23 at 19:08
  • Thank you for new example @JordanBrough. Yes, it works in older versions too, I use it in my projects – mechnicov May 25 '23 at 19:50