Stubbing Method Chains with Mocha

Ryan Foster, Former Developer

Article Category: #Code

Posted on

Yesterday, I came across a head scratcher trying to stub my tests with Mocha. I checked the documentation. No luck. I tried a few Google searches. Nothing obvious. The next logical step: ask around the development team. Their savvy didn't disappoint. Here's the breakdown on how to stub method chains using Mocha.

Let's say you have a blog post class with a scope to order by creation date:

class Post < ActiveRecord::Base
  scope :recent, order("posts.date DESC")
end

and you want to use it in a controller like this:

@latest_post = Post.recent.first 

First, you need to test. And you don't want the controller tests to go all the way down to the database. Let the model tests handle that. Here is how to stub the method chain:

@post = Post.new

first = stub
first.stubs(:first).returns(@post)
Post.stubs(:recent).returns(first)

You actually end up writing this solution in reverse. Stub the Post class with :recent like normal. For the return, you'll need a mock object that responds to :first. So you create the mock and stub out the :first method.

Trim up the syntax with this shorthand:

first = stub(:first => @post)
Post.stubs(:recent).returns(first)

or inline:

Post.stubs(:recent).returns(stub(:first => @post))

Note that you can't use the shorthand if the stubbed methods require parameters.

Let's take it even further (but you probably shouldn't). A longer method chain:

Post.recent.first.partial_text.upcase

can be stubbed:

Post.stubs(:recent).returns(stub(:first => stub(:partial_text => stub(:upcase => 'PARTIAL TEXT'))))

However, if you find yourself writing a stub chain like this, sit back and take a sip of coffee. It's probably time for some refactoring.

Related Articles