Fixtures, mock objects or in-memory ActiveRecord objects?

Posted by James Mead Sat, 23 Dec 2006 20:00:00 GMT

Wilson Bilkovich has posted an article about mocking ActiveRecord objects.

A new method mock_model is defined that builds a mock object which will respond the same way as a real ActiveRecord object. As I understand it, this means he can replace…

  @campaign = mock("campaign")
  @campaign.stub!(:is_a?).and_return(true)
  @campaign.stub!(:new_record?).and_return(false)
  @campaign.stub!(:id).and_return(rand(1000))

with…

  mock_model :campaign

Although I agree with him that using fixtures is not a good idea, why not use a real ActiveRecord object…

  @campaign = Campaign.new

Sometimes due to the way ActiveRecord couples your models to the database, it becomes essential to have a model in the database and not just in memory. In which case why not just do this…

  @campaign = Campaign.create!

I’ve written up a couple more thoughts here and here.

Tags , , , , , ,  | 1 comment

Ruby stubbing techniques

Posted by James Mead Sat, 16 Sep 2006 15:05:00 GMT

Jay Fields is writing a great series of articles about stubbing in Ruby. Many of the techniques are similar to ones we developed at Reevoo and led to the creation of Mocha, although I have to admit we didn’t take the use of OpenStruct as far as he did.

A while ago I did attempt to write a series of articles explaining the evolution of Mocha, but I got it a bit tangled up with how it could be used to avoid the use of fixtures – so it petered out. Jay’s articles are much more useful.

Tags , , , , , ,  | no comments

Injecting mocks (the Mocha way)

Posted by James Mead Fri, 01 Sep 2006 06:26:00 GMT

I thought I’d compare and contrast the way Gluttonous was injecting mocks and how you can do it with Mocha.

Gluttonous’ way

  def test_process_exit
    delegate_methods_to_mock!(RailsFCGIHandler, :close_connection) do
      fcgi = flexmock()
      fcgi.should_receive(:close_connection)
      @handler.mock = fcgi
      @handler.stubs(:when_ready).returns(:exit)
      @handler.process!
    end
  end

Using Mocha

  def test_process_exit
    @handler.expects(:close_connection)
    @handler.stubs(:when_ready).returns(:exit)
    @handler.process!
  end

I think it’s a bit more readable and you don’t need the block construction which starts becoming a nuisance when you need to stub methods on multiple classes.

Tags , , , , , , , , ,  | 4 comments

Mocks on Rails

Posted by James Mead Thu, 31 Aug 2006 04:25:00 GMT

It’s great to see that Gluttonous has been playing with Mocha. Like many people, he’s found the ability to mock or stub class methods particularly useful – and this is one of the key differentiators between Mocha and other Ruby mocking libraries.

He’s been trying to improve the test coverage for Rails and submitted this patch where there is an interesting discussion1 about why he would prefer to use Mocha. Interestingly, there has also been some recent discussion on the RSpec mailing list about adding Mocha-like functionality.

On a different note, in his article Mocks for Speed, Gluttonous draws attention to one of the advantages I have previously mentioned of using mocks extensively to write unit tests that test a class in isolation – namely a fast build.

1 We have now released Mocha under the MIT license so it can be used for testing within Rails.

Tags , , , , , , , , , , ,  | 4 comments

Fed up with Rails fixtures? (part two)

Posted by James Mead Mon, 14 Aug 2006 03:24:00 GMT

Previously I described how you can write unit tests for ActiveRecord models without using fixtures – just using models in memory. Although only a limited subset of tests can be written this way, it’s often overlooked.

Another technique is to use constructor-based dependency injection (DI) in combination with mock objects. However, thanks to ActiveRecord::Associations::AssociationProxy#raise_on_type_mismatch it isn’t straightforward to use mocks for associated models. However, DI can be used effectively for ActionController controllers:

class ArticleController < ApplicationController
  def initialize(article_class = Article)
    super()
    @article_class = article_class
  end
  def show
    @article = @article_class.find(params[:id])
  end
end

The controller constructor defaults to using the real Article model class, but in the test we’re going to inject a mock object in its place. You could use one of the many mocking libraries for this, but I’m going to use our home-grown one, Mocha, in a “functional” controller test.

require File.dirname(__FILE__) + '/../test_helper'
require 'article_controller'

class ArticleController; def rescue_action(e) raise e end; end

class ArticleControllerTest < Test::Unit::TestCase
  def setup
    @article_class = mock()
    @controller = ArticleController.new(@article_class)
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
  end 
  def test_should_display_article
    article = Article.new
    @article_class.expects(:find).with('1').returns(article)
    get :show, :id => 1
    assert_template 'show'
    assert_equal article, assigns(:article)
  end
end

I wrote this at about 6am on the train from Durham to London without the aid of a coffee, so it may not make much sense!

Tags , , , , , , , ,  | 5 comments

Rails fixtures - help or hindrance

Posted by James Mead Tue, 27 Jun 2006 20:13:00 GMT

So it looks like Ben has finally shamed me into writing my annual blog entry.

My colleague Chris recently pointed me at a report from RailsConf 2006. It didn’t surprise me to read that fixtures are the part of Rails with which the core team are most unhappy—apparently about half of them don’t even use fixtures.

In the early stages of developing a Rails application, the fixtures functionality built into Test::Unit::TestCase by ActiveRecord can be pretty useful. However, as the application grows it becomes harder and harder to stop fixtures getting in your way. One of the more obvious problems is your test suite taking longer and longer to run. This is often tackled by making performance improvements like using transactional fixtures or using an in-memory database. But if your application is continuing to grow, it really only buys you time.

I think the performance problem is merely the most visible symptom of a deeper malaise. Data in fixture files…

  • is effectively global—unless you are very strict about having a single role for each fixture record, it’s very easy to share fixtures across many unrelated tests because it is convenient and not because its sensible. This is essentially a coupling problem and leads to brittle tests where changing one attribute of one fixture record breaks a bunch of tests.
  • reduces the self-contained readability of tests—personally I dislike even having to scroll to the top of the TestCase to find the setup method, let alone having to navigate to another file.
  • leads to tedious and error-prone setup when you have complex object graphs and is just as hard to read/navigate afterwards.
  • leaves data in the database after the last test in a TestCase—potentially interacting with a subsequent TestCase.
  • encourages unit tests that interact with the database—although this is what slows tests down, you should also realise it means the unit that’s being tested includes not only your application class, but many lines of ActiveRecord code as well as the underlying database. Given that this 3rd party code is likely to be well-tested, it seems like overkill to include it in your own application’s unit tests.
  • is artificial data—it gets directly inserted into the database bypassing all business logic and validation. So it’s very easy to end up testing impossible scenarios or even getting false positives.

So what’s a better way to write unit tests? That’ll have to wait until next year ;-)

Tags , ,  | 1 comment