Testing with Mock Objects in Rails
UPDATE: May 24th, 2007
This post is now woefully out of date; since it was written, several excellent mocking libraries have been released, and some testing frameworks include mocking functionality of their own. Watch the Four Labs blog for an updated version of this, coming soon!
—–
My last two posts on Ruby on Rails both dealt with using external resources in your application (Akismet and the Yahoo Term Extractor, respectively). One difficulty that often arises when code relies upon external pieces is testing - third parties often frown on the high-volume, rapid hits that a comprehensive test suite generates, and test runs can be dramatically slowed when they rely on resources outside the local machine or network. Luckily, Rails has built-in support for a technique that resolves both of these difficulties: mock objects.
The basic idea is that, for the bulk of testing, you don’t hit the remote resource itself. Instead, you define a mock version of it that responds to the appropriate calls, and (for a limited set of inputs) produces the same output that the real resource would.
An Example
The following is an example of using a mock object to test application functionality that relies on the TagExtractor class we looked at in a previous post. In the test/mocks/test folder, we created a file named tag_extractor.rb that contains the following:
class TagExtractor
def self.extract(text)
unless text.blank?
['tag1', 'tag2', 'tag3']
else
[]
end
end
end
When you run the tests for your application, this TagExtractor class definition will override the normal one, and you’ll get an array consisting of tag1, tag2, and tag3 for any text you pass to the extract method (or an empty array, if you pass nothing). Once that’s set up, you can test the rest of your application as if the TagExtractor class were actually calling out to Yahoo.
But…
Of course, we’ve now introduced a problem into our testing - we’re no longer testing the real TagExtractor class. As things currently stand, every call to TagExtractor.extract from within the test environment will go to the mock object, so we have no way of verifying that our code to interact with the Term Extractor service is correct.
The solution to this problem is simple, and is due to the same characteristic of Ruby that makes testing with mocks so easy. Basically, if multiple definitions exist for a class (or method, etc.), the last one the interpreter finds wins. In the case above, the real TagExtractor class definition is read before the mock definition, so the mock definition is used in tests. To get the real definition back, we just need to re-insert it into the stream after the mock definition is read. So, in the unit test file for the TagExtractor class, we do:
require 'models/tag_extractor'
class TagExtractorTest < Test::Unit::TestCase
...
end
With that, the TagExtractor unit tests use the real definition (and call out to the real web service), while any other unit tests and functional tests that exercise the tag extractor functionality use the mock definition.
November 30th, 2006 | By Ben Scofield - Senior Developer
Posted in Development

Why go to that much trouble when
I was watching a little NBA on ESPN last night and hadn’t had a chance to store up some buffer on my DVR, so I was forced to watch the commercials (woe is me). 