[rspec-users] Applying an rspec matcher against the elements of a collection

Pete Hodgson ohthepete at gmail.com
Tue Feb 23 13:18:34 EST 2010


Hi all,
I've tried to figure out whether rspec has any features to make it
easier to make assertions against the elements of a collection, but I
haven't had any luck finding anything so far. I thought I'd explain
the problem here, and propose a potential feature that might mitigate
it.

Let's say I have a Person class:

class Person < Struct.new( :name, :age )

  VOTING_AGE = 18
  def self.get_voters( people )
    people.reject{ |person| person.age < VOTING_AGE }
  end

end

As you can see we have a method here which filters a collection of
people, returning only those people old enough to vote. If I were to
test this method in rspec I might write:

describe 'Person vote filtering' do
	it 'filters out people younger than voting age' do
		people = [
			Person.new( 'jenny', 18 ),
			Person.new( 'dave', 12 ),
			Person.new( 'paul', 19 ),
			Person.new( 'lisa', 17 )
		]

		voters = Person.get_voters( people )

		voter_names = voters.map{ |p| p.name }
		voter_names.should == ['jenny','paul']
	end
end

This works, but having to manually pull out the voter names into a
seperate collection just in order to check who was filtered and who
wasn't has always seemed clunky to me. What I would prefer is to be
able to check whether the collection contains person who matching my
expectations. Say I have a custom matcher:

Spec::Matchers.define :be_named do |expected|
	match do |actual|
		actual.name == expected
	end
end

Then I'd like to be able to write something like

voters.should( have(2).people )
voters.should( have_one_that( be_named('jenny') ) )
voters.should( have_one_that( be_named('paul') ) )

or even:

voters.should( have_elements_that(
	be_named( 'jenny' ),
	be_named( 'paul' )
)

To me this is a lot clearer - although the method names and how
they're composed into the DSL could clearly use some work ;).

Now, for the trivial case I've been using as an example it would
probably be overkill, but I often find myself writing fairly
convuluted code at the end of a test just to figure out whether a
collection contains an element that matches some complex predicate. It
seems to me that if rspec had the generic ability to apply matchers to
the elements of a collection it would raise the level of
expressiveness for this kind of tests.

Thoughts? Does Rspec already support something like this that I'm just
not aware of? If I were to write a patch implementing this would it
have any chance of being accepted?

Cheers,
Pete


More information about the rspec-users mailing list