March 20, 2007

RSpec:Rails - How to mock partials when testing a View.

When developing Ruby on Rails (RoR) applications I prefer to use RSpec over the built in testing framework provided with RoR. The biggest benefit is that RSpec easily allows you to test your application layers in isolation (e.g. when testing a Controller you should not be interested in testing the Model). So with that in mind when I am testing a View I am only interested in testing that View and not any of the potential partials that View may try to render.

Suppose we have the View /example/hello_world.rhtml:

<div id="person">
<h2 class="title">Hello <%= @person.name %></h2>
<%= render :partial => 'address', :locals => {:person => @person} %>
</div>

Often in RSpec when testing a view such as this the spec ends up testing the partial as well. This is less than desirable because any change to the partial could break test for other views that render that partial. After some investigation I discovered that I could mock the calls to <%= render :partial => 'address'... %>. Have a look at the following spec:

specify 'should call mock and not the actual partial' do
person = mock('person')
person.should_receive(:name).and_return('Regan')
assigns[:person] = person

# Mock the call to 'render_partial'...
@controller.template.should_receive(:render).with(:partial => 'address', :locals => {:person => person})

render '/example/hello_world'
response.should_have_tag(:h2, :content => "Hello Regan")
end

You can grab a handle to the ActionView through @controller.template as is done in the example above. Then I mock the call to the 'render' method only when the 'address' partial is requested.

As you can see my spec only needs to be concerned with testing the /example/hello_world.rhtml View. The partial will have it's own separate spec. The side effect is that my view specification becomes much more manageable and far less brittle.