I’d like to point out a really nice testing practice that I’ve been loving more and more lately.

Just about every mature testing framework out there supports the concept of custom matchers, which provide us with the ability to define our very own assertions seamlessly into the tests. Even though this ability is quite old, we don’t see it used too often and I think that’s a shame. I’ve seen this practice heavily used in the mind expanding GOOS book and just now am starting to realize its awesomeness.

Note: examples in this post are shown in Ruby using RSpec’s matchers but the concept is pretty much identical (as can be seen for example in Java’s Hamcrest Matchers).

Matchers 101

Creating your own matcher usually means creating a Matcher class that performs the assertions, supplies human readable error messages and a nice constructor.

Here’s an example from the RSpec documentation:

RSpec::Matchers.define :be_a_multiple_of do |expected| match do |actual| actual % expected == 0 end end

Matchers increase readability and intent

As you should know, one of the most important rules for design is Reveals Intent. Take a quick look here, which way do you think reveals more intent?

# This response['X-Runtime'].should =~ /[\d\.]+/ # .. or this? response['X-Runtime'].should be_a_number

Also, which error message do you prefer? “expected false to be true” or something along the lines of “expected comment to be anonymous”?

Matchers create robust tests

The most important advantage of all is how using matchers easily allows you to steer away from fragile tests which are the bane of a lot of testing efforts. The mark of good tests is that a change in your code doesn’t require you to perform changes in multiple tests that don’t really care for the change. Take this code for example:

expected_comment = Comment.new(anonymous: true, user: "the dude", reply_to: nil) commentor.should_receive(:add).with(expected_comment)

This might seem like a standard test, but that’s not really the case. A test should assert for a single piece of knowledge, and this test actually checks several. If the purpose of this test is to check the behavior of anonymous comments, why should it change if we no longer allow replies? Or if we no longer require users for posting comments?

The magic of matchers is exactly here. You create a new matcher to check specifically the aspect your test cares about and boom, you’re decoupled!

commentor.should_receive(:add).with(anonymous_comment)

This simple change makes your tests DRY and cool.

Happy testing!

Your should subscribe to my feed or follow me on twitter!