Stubbing Method Chains with Mocha
Ryan Foster, Former Developer
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")
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
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(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:
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.