Custom RSpec-2 Matchers

RSpec is one of my favorite tools. I have literally fallen in love with this fantastic BDD library, especially with its second version. While using RSpec I
realized it teaches me how to write tests. Yes, exactly – learning RSpec DSL, its syntax and structure of spec examples you actually learn the best practices
in writing tests. RSpec, despite many built-in matchers, comes with a DSL for defining your own, custom matchers. It’s so easy that you’re not gonna believe
this.

Basics

In RSpec matchers are nothing but methods available in the context of an example. You use them to make sure that a given expectation is met. There are
many matchers that come with RSpec for instance here is how you can use the respond_to matcher:

It’s so clean and beautiful that I probably don’t have to explain what this piece of code does, right?

Tip: When you call describe with a class as an argument, RSpec will automatically create an instance of that class and make it available via subject
method. Subject is also the default context of an example block. That’s why we don’t have to write “subject.should respond_to(:gsub)”, because by default “should” or “should_not” is called on the subject.

Alright, for a list of available matchers check out the official docs. Let’s
focus on writing our own matchers. If you’re wondering why you would need to do that, let me show a simple example of a User model spec:

Now, you probably can imagine that almost identical code could be used in many other cases for many other model classes. Those 6 lines of code can be written as 1. You just need a custom matcher.

Defining a custom matcher is simple. Let’s start with a basic one that checks if a given model instance has validation errors:

And we can use it like that:

It covers only the first expectation, but it’s a good starting point.

Chaining

The second expectation in the example is to see if the correct validation error message is set. It’s possible to run matchers in a chain so let’s see how we can implement chaining in our custom matcher:

It’s really that simple. Now the matcher checks two things and returns true only if the message exists and if it matches the expected one.

Let’s use it:

Nice! One line instead of six. But that’s not everything, with a failing spec
failure messages might look like that:

It’s automatically generated by RSpec based on the matcher name. It’s ok, but notice that it won’t tell us if the error message was incorrect. That’s why we need to set custom failure messages.

Custom failure messages and it’s done!

It is really recommended to use meaningful failure messages. We need to set 2 types of them, first one for “should” and second one for “should_not” expectations. The idea is that if there is an error and the message is not correct, we need to show that information in the failure output.

So, our complete matcher looks like that:

Now if we run our example and it fails because an error message doesn’t match the expectation, we will get following failure message in the output:

Summing up

As you can see implementing custom RSpec matchers is easy
trivial and it’s a highly recommended practice. There are plenty of use cases where you want to write custom matchers. It makes your specs clean and even
more readable and what’s most important it keeps your spec’s code DRY and extendable.

Here are some resources if you want to learn more:

  • Geraldo Lopes de Souza

    Thanks for the article. I’ve learnt the it { should ..} here.
    About the example: email could have others errors , what I have done a simple matcher 
    model.has_error_message(:email, ‘Email is invalid’) 
    Of course that would defeat the use of chain that you wanted to show.

    Keep up the good work :)

  • Frédéric

    As the latest RSpec release notes tell (http://blog.davidchelimsky.net/2012/01/04/rspec-28-is-released/#),  the DSL-based matchers are slower compared to their class-based counter-parts (https://github.com/rspec/rspec-expectations/blob/master/benchmarks/matcher_dsl_vs_classes.rb#.)

    You probably should consider to use the class-based version when implementing a matcher that will heavily be used in your specs.

    • Frédéric

      Woops, missed the post date (2011-01-14, we are now in 2012…).

      I should have started with “As a laster RSpec release notes has showned…”

      • http://solnic.eu/ solnic

        Right, seems like I wrote it *exactly* one year ago :)

    • http://blog.davidchelimsky.net David Chelimsky

      I still use the DSL for custom matchers. Speed is cumulative. The benchmarks I ran (https://github.com/rspec/rspec-expectations/blob/master/benchmarks/matcher_dsl_vs_classes.rb) showed that 1k invocations of a simple custom matcher took 0.3 secs when generated by the DSL, whereas it was closer to 0.004 for the same using a class. Dramatic difference, sure, but unless  you’ve got 1k invocations of your custom matchers, you won’t perceive any difference.

  • David Mendiola

    Thanks!!