rspec/rspec-expectations — pull request #586 by jfelchner (2014-06-19 21:29:49+00:00)
Title: Feature: Add a 'deeply_include' matcher

We've had many cases in the past where, when comparing a Hash against another
Hash, we want the 'include' comparison to continue onto any subhashes in the
original Hash.

For example, with the current implementation:

```ruby
it 'knows when one hash is included in another' do
@actual_hash = {
foo: 'bar',
baz: {
'hello' => 1,
'goodbye' => 2,
}
}

expect(@actual_hash).to include(
foo: 'bar',
baz: {
'hello' => 1,
'goodbye' => 2,
}
)
end
```

This passes correctly, but for us, there are often times when the Hash that is
returned contains keys that we don't necessarily care about, and sometimes those
keys are nested a level or two deep.

For example, if we are testing that we receive the proper data from a response
Hash from an API, there may be a `request_id` in there which is unique and that
we don't actually care about, but there are other items which we want to match
against.

Using the current implementation we'd have to do something like this:

```ruby
it 'knows when one hash is included in another' do
@response_hash = {
products: [
{
guid: 'bbd7fa10f15bb771c33b32545ca95e16',
name: 'Rubber Duckies',
category: 'Toys',
materials: [
'Yellow Lead-Free Paint',
'Rubber',
'Duckies'
]
}
]
}

first_product = @response_hash['products'][0]

expect(first_product).to include(
name: 'Rubber Duckies',
category: 'Toys',
)

expect(first_product['materials']).to include([
'Rubber',
'Duckies'
])
end
```

With the changes in this commit, we've added a `deeply_include` matcher which
would allow the above example to be written thusly:

```ruby
it 'knows when one hash is included in another' do
@response_hash = {
products: [
{
guid: 'bbd7fa10f15bb771c33b32545ca95e16',
name: 'Rubber Duckies',
category: 'Toys',
materials: [
'Yellow Lead-Free Paint',
'Rubber',
'Duckies'
]
}
]
}

expect(@response_hash).to deeply_include(
products: [{
name: 'Rubber Duckies',
category: 'Toys',
materials: [
'Rubber',
'Duckies'
]
}]
)
end
```

Obviously this implementation is... very poor, but I wanted to get the
conversation started and I'll make any necessary changes to get it in line with
what you all would like.

Our initial thoughts are that we may just create a subclass of the `Include`
matcher for the `deep` implementation.

Additionally the matcher will need to be refactored so that we can implement
a better recursive strategy than instantiating new instances on every recursive
loop.