On making custom matchers in RSpec 1.

This article is deprecated.I've marked this article as deprecated. It is either because the gem or plugin discussed in this article is no longer maintained, or I've changed my opinion on it. This article is here only as an historic reference.

RSpec, Shoulda, and custom matchers

I have been playing with the matchers that Thoughtbot's Shoulda provides, and they are very cute!

For instance, a controller can be easily tested like this:

describe ArticlesController do

  integrate_views

  describe "the index action" do

    before :each do
      stub(@article = Article.new).id { 1337 }
      mock(Article).all { [@article] }
      get :index
    end

    it { should route(:get, articles_path).to(:action => :index) }
    it { should respond_with(:success) }
    it { should render_template(:index) }
    it { should_not set_the_flash }
    it { should assign_to(:articles).with([@article]) }

  end
end

As you see I'm a fan of rr as well. I love the sleek and concise syntax it offers, just as the Shoulda matchers.

So, now what?

Well, I don't like to spec views separately. It's too much of a drag to set up all the required instance variables, because there tend to be a lot of them. That's why I use the integrate_views command. I do want to spec some essential elements rendered in the view. Just knowing that no exceptions were thrown is not always good enough.

The solution is the have_tag matcher. This is actually a wrapper around assert_select, allowing you to use CSS selectors to check your view. I ended up testing links to certain actions, to ensure all the actions are reachable for the user. For example:

it { should have_tag("a[href=#{article_path(@article)}]") }

Yuck! That is a lot of noise! The whole use of RSpec is to create human readable tests. Also, there is a lot that can go wrong here. I will easily forget one or more of those differently shaped brackets. I want to write something like:

it { should have_link_to(article_path(@article)) }

Much cleaner! Making a matcher for this isn't that difficult:

module CustomLinkMatcher

  include Spec::Rails::Matchers

  def have_link_to(url)
    AssertSelect.new(:assert_select, self, "a[href=#{url}]")
  end

end

Don't forget to activate it, though:

Spec::Runner.configure do |config|
  config.include CustomLinkMatcher
end

So, why go through all this trouble? Why should I even care? I mean, the have_tag selector isn't that unreadable, just a bit cluttered. Well, I found that the ease of which you can type specs is directly related to how extensive you test. If a test is difficult to type, hard to read, or feels repetitive, most people (with possible exception of Bryan Liles ;) ) will get annoyed with it and don't do it anymore.

To give myself as an example. I never cared much about heckle. Heckle could mutate so much code, I soon stopped caring. Now, with the matchers Shoulda gives me, I like running Heckle! I know keeping Heckle happy shouldn't be a goal, and I accept certain things Heckle will heckle me about even now, but writing specs like this really made my tests much more robust!

So, in conclusion: if you find yourself hating to write certain specs, try to refactor your specs so it becomes easy and fun! Try to write tests as you would like your tests to be written. Testing should be fun! Keep it that way! Use every gem, tool and technique you have to do so!

comments powered byDisqus