Testing an array of objects with RSpec have_attributes

Ben Pickles

After recently discovering RSpec’s --next-failure option I’ve just happened upon the have_attributes matcher which can help turn many expectations into a single, more readable statement.

In the past when checking an array of objects I’ve manually written out each expectation, something like this:

expect(items[0].id).to eql(1)
expect(items[0].name).to eql('One')
expect(items[1].id).to eql(2)
expect(items[1].name).to eql('Two')

But have_attributes lets you check an object’s properties against a hash, so the above can be re-written as:

expect(items[0]).to have_attributes(id: 1, name: 'One')
expect(items[1]).to have_attributes(id: 2, name: 'Two')

Even better, have_attributes can be combined with match_array to get this:

expect(items).to match_array([
have_attributes(id: 1, name: 'One'),
have_attributes(id: 2, name: 'Two'),
])

In one particular case I also wanted to check that the correct class was being returned, which is simple as it’s just another method call:

expect(items).to match_array([
have_attributes(class: Foo, id: 1, name: 'One'),
have_attributes(class: Bar, id: 2, name: 'Two'),
])

The next thing I tried felt quite natural though I didn’t expect it to work:

expect(items).to match_array([
have_attributes(
class: Foo,
id: 1,
name: 'One',
price: have_attributes(
cents: 123,
currency: 'GBP',
),
),
have_attributes(
class: Bar,
id: 2,
name: 'Two',
price: have_attributes(
cents: 456,
currency: 'USD',
),
),
])

It turns out this almost exactly matches the examples from the docs — I guess I wasn’t paying much attention all those years ago when RSpec announced composable matchers.


Originally published at www.benpickles.com

Ben Pickles

Written by

Listener of Zappa • writer of Ruby and JavaScript • creator of https://github.com/benpickles/peity • leader of thought

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade