38

I have an array of hashes, which for the sake of argument looks like this:

[{"foo"=>"1", "bar"=>"1"}, {"foo"=>"2", "bar"=>"2"}]

Using Rspec, I want to test if "foo" => "2" exists in the array, but I don't care whether it's the first or second item. I've tried:

[{"foo" => "1", "bar" => "2"}, {"foo" => "2", "bar" => "2"}].should include("foo" => "2"))

But this doesn't work, as the hashes should match exactly. Is there any way to partially test each hash's content?

1
  • 1
    [{"foo" => "1", "bar" => "2"}, {"foo" => "2", "bar" => "2"}].flat_map(&:to_a).should include(["foo","2"]) will work also. Commented May 22, 2014 at 19:59

5 Answers 5

51

how about?

hashes = [{"foo" => "1", "bar" => "2"}, {"foo" => "2", "bar" => "2"}]
expect(hashes).to include(include('foo' => '2'))
Sign up to request clarification or add additional context in comments.

3 Comments

And if you want all instead of any, well, you can use all: expect(hashes).to all(include('foo' => '2'))
This works, but I would phrase it as: expect(hashes).to include(a_hash_including("foo" => "2"))
19

Use Composable Matchers

hashes = [{"foo" => "1", "bar" => "2"}, {"foo" => "2", "bar" => "2"}]
expect(hashes)
  .to match([
    a_hash_including('foo' => '2'), 
    a_hash_including('foo' => '1')
  ])

Comments

12

You can use the any? method. See this for the documentation.

hashes = [{"foo" => "1", "bar" => "2"}, {"foo" => "2", "bar" => "2"}]
expect(hashes.any? { |hash| hash['foo'] == '2' }).to be_true

1 Comment

fwiw i used .to be_truthy Thank you for the clean solution.
7

you can use composable matchers

http://rspec.info/blog/2014/01/new-in-rspec-3-composable-matchers/

but I prefer to define a custom matcher like this

require 'rspec/expectations'

RSpec::Matchers.define :include_hash_matching do |expected|
  match do |array_of_hashes|
    array_of_hashes.any? { |element| element.slice(*expected.keys) == expected }
  end
end

and use it in the specs like this

describe RSpec::Matchers do
  describe '#include_hash_matching' do
    subject(:array_of_hashes) do
      [
        {
          'foo' => '1',
          'bar' => '2'
        }, {
          'foo' => '2',
          'bar' => '2'
        }
      ]
    end

    it { is_expected.to include_hash_matching('foo' => '1') }

    it { is_expected.to include_hash_matching('foo' => '2') }

    it { is_expected.to include_hash_matching('bar' => '2') }

    it { is_expected.not_to include_hash_matching('bar' => '1') }

    it { is_expected.to include_hash_matching('foo' => '1', 'bar' => '2') }

    it { is_expected.not_to include_hash_matching('foo' => '1', 'bar' => '1') }

    it 'ignores the order of the keys' do
      is_expected.to include_hash_matching('bar' => '2', 'foo' => '1')
    end
  end
end


Finished in 0.05894 seconds
7 examples, 0 failures

1 Comment

If you want to make it work with a hash that uses symbols, you can do this: RSpec::Matchers.define :include_hash_matching do |expected| match do |array_of_hashes| array_of_hashes.any? do |element| expected = expected.stringify_keys element = element.stringify_keys element.slice(*expected.keys) == expected end end end
0

If separate testing of hashes is not a strict requirement I would do it this way:

[{"foo" => "1", "bar" => "2"}, {"foo" => "2", "bar" => "2"}].map{ |d| d["foo"] }.should include("2")

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.