rspec_on_rails render_and_receive_matcher
on November 14, 2007 @ 12:10 AM

Earlier today my pair and I saw a repetitious pattern in our view specs when we were using helpers. It looked similar to the following:

1
2
3
4
5
 it "displays foo" do
    template.should_receive(:bar).with(@foo).and_return(%|<p id='bar'></p>|)
    render_it
    response.should have_tag("p#bar")
 end

The repetition was in the three lines:

  • setup a simple should receive and return
  • make the call to render
  • verify the response had the returned element

Tonight I spent some time spiking a way to cure this repetition. I’m a bigger fan of verbosity in specs over DRYness, but I think my exploration gives us something good. The above example now looks like:

1
2
3
4
5
 it "displays foo" do
    during_render do
      template.should receive_and_render(:bar).with(@foo)
    end
 end

The receive_and_render method sets up a should_receive expectation on the template. For completeness there is also a stub_and_render method. As you might guess this method sets up a stub! on the template.

This doesn’t just work on the template object. It works on any object, but you should probably only use it on objects that show up in the view.

script/plugin install

script/plugin install \    
  http://continuous.rubyforge.org/svn/trunk/rspec/matchers/rspec_on_rails_render_and_receive

Final Thoughts

This in an experiment. As I am learning more about the spirit of rspec and it’s organization there are a few things I want to change with this. Right now this is implemented as a matcher which doesn’t feel quite right, but time will tell. So in the meantime… Happy rubying!


  1. David Chelimsky 11.14.07 / 06AM

    Cool idea, but it only works if you want a p tag. How about parameterizing that a bit. Something like:

    template.should receive(:bar).with(@foo).and_render(“p#bar”)

    That way you get the the terseness you’re looking for with the flexibility of rendering arbitrary tags. At that point you could probably exploit nested with_tag:

    template.should receive(:bar).with(@foo).and_render(“ul#bar”) do with_tag(“li#baz”,”foo-ness”) end

    WDYT?

  2. Bill Bereza 11.14.07 / 15PM

    I’ve always just used template.should_receive.and_return calls to verify that something gets called and that it’s results are placed into the template in the right place. The actual string returned has no real purpose except as a marker, so I’ve always just made up a unique tag for each different expectation.

    So, in my (haml) template I might have a something like:

    .date= short_date(@date)

    And my spec setup would be:

    template.should_receive(:short_date).with(@date).and_return(‘<short_date />‘)

    and the spec would be:

    response.should have_tag(’.date short_date’)

    I think for view specs, being able to verify where in the template the result gets placed is just as important as whether the call is made.

  3. Zach Dennis 11.18.07 / 17PM

    @David,

    I like separating out the and_render how you have it because it makes it read a lot nicer. After talking with you about the do/end blocks it’s clear that you’re throwing the situation where you care where something is rendered, which is one of the key things Bill mentioned in his comment as well.

    @Bill,

    You’re right on the money with the fact the actual string rendered is just a marker, so it’s content does not really matter at all. Needing to determine where the result gets placed is very important, and after talking with you about this doing something like response.should receive(:foo).and_render.inside(’.date .short_date’) will be a good to solution for this.

    I’ll see what I came come up with, thx for your comments.