Mocking out your Rails helpers in helper specs

RSpec provides some pretty good tools for mocking your objects in Rails test specs.

Mocks allow you to set expectations on what kind of messages are passed to your objects, and what values they pass back in return. Among other things, this brings the advantage that your test/example can be truly focused on one unit of code, because the data that the mocks (or stubs) return does not rely on the implementation of the objects or methods that they imitate.

In most cases, mocking with RSpec in your Rails tests is pretty straightforward - you just create the mock object and set your expectations of it:

# mock_model is a convenience provided by rspec_on_rails
my_mock = mock_model(User)
my_mock.should_receive(:authenticate).with('user', 'pass').and_return(true)

Alternatively, you can create partial mocks or stubs on real objects or classes:

# partial stub for a real object
u = User.new
u.stub!(:name).and_return('Phred')

# partial mock for a class method
User.should_receive(:find).and_return(u)

In all of these cases, there is a clear recipient for the mocking or stubbing treatment - a mock object, or real object, or a class. This is what you will mostly use in your model and controller specs.

However, if you are in the specs for your helpers, it’s not self evident what the recipient should be. Here’s an example for a helpers file with two helpers, one calling the other:

module ApplicationHelper
  def user_list(users)
    users.each { |u| user_summary(u) }
  end

  def user_summary(user)
    open :div, {:class => 'user_summary'} do
      open :p, user.name
      open :p, user.birthday
    end
  end
end

These helper methods all belong inside a module, and are not attached to any object or class like the ActiveRecord models in the above examples. So how do we mock a helper? Check it out:

require File.dirname( __FILE__ ) + '/../../spec_helper'

describe ApplicationHelper, "user printing helpers" do
  include ActionView::Helpers
  include Haml::Helpers

  it "should call the user_summary helper for each user object in the array passed to user_list" do
    user = mock_model(User) do |u|
      u.stub!(:name).and_return('Phred')
      u.stub!(:birthday).and_return('April')
    end
    users = []

    5.times do
      users << user
    end

    # This is the clincher!
    self.should_receive(:user_summary).exactly(5).times

    user_list(users)
  end
end

In your helper specs, the self object is what you can use to mock and stub your helpers. It’s that simple. What is self? I’m not really quite sure. It looks like a dynamically created subclass of Spec::Rails::Example::HelperExampleGroup that is particular to the spec example currently being run. Whatever it is, it gets the job done for us.

If you also need to mock or stub your helpers in view specs, you can use the @controller.template object:

@controller.template.stub!(:a_helper_method).and_return(true)

Check out Jack Scrugg’s article for more detail about this.

© 2008-2024 Tim Riley. All rights reserved.