RSpec's hash_including matcher does not support nesting

Updated . Posted . Visible to the public.

You can not use the hash_including argument matcher Show archive.org snapshot with a nested hash:

describe 'user' do
  let(:user) { {id: 1, name: 'Foo', thread: {id: 1, title: 'Bar'} }

  it do 
    expect(user).to match(
      hash_including(
        id: 1, thread: {id: 1}
      )
    )
  end
end  

The example will fail and returns a not very helpful error message:

expected {:id => 1, :name => "Foo", :thread => {:id => 1, :title => "Bar"}} to include hash_including(:id => 1, :thread => {:id => 1})
Diff:
  @@ -1,2 +1,4 @@
-[hash_including(:id=>1, :thread=>{:id=>1})]
+:id => 1,
  +:name => "Foo",
  +:thread => {:id=>1, :title=>"Bar"},

Instead you need to do something like this:

describe 'user' do
  let(:user) { {id: 1, name: 'Foo', thread: {id: 1, title: 'Bar'}} }

  it do
    expect(user).to include(
      id: 1,
      thread: hash_including(id: 1)
    )
  end
end

Or nest multiple hash including:

describe 'user' do
  let(:user) { {id: 1, name: 'Foo', thread: {id: 1, title: 'Bar'} }

  it do 
    expect(user).to match(
      hash_including(
        id: 1, thread: hash_including(id: 1)
      )
    )
  end
end

Note:

  • RSpec will highlight array_including and hash_including matches with red even they should be green if a test fails. You need to find out manually where the result differs from the expectation.

  • If you want to match an ActiveRecord relation you have to map the attributes and mutate for indifferent access or use string hash keys

users.map(&:attributes).to include(hash_including('name' => 'Some name'))
Last edit
Emanuel
License
Source code in this card is licensed under the MIT License.
Posted by Emanuel to makandra dev (2017-07-20 14:42)