Mock objects and life expectancy

June 26, 2005

· ·

For my sins, I’m a smoker and caffeine-addict. On a previous project, before I discovered the mock-objects religion, it took so long to run unit tests before a code check-in that I had time to nip downstairs, get a coffee, have a quick cigarette and be back upstairs before the green bar was halfway done. I knew things were bad when I realised I was on 20+ check-ins per day.

I think that the biggest advantage of using mock objects is that your tests run quicker. I find that the quicker I can run all the tests, the braver I become in fixing any broken windows that have crept into the code. But there are other significant advantages too: design decisions can be defered until you have more knowlege about the domain; classes tend to end up less tightly coupled; girls throw themselves at you in the street. Well, perhaps two of those three things are true.

An example from our codebase using the Schmock mock objects library that we developed while writing our code here.

Although we’re programming in Rails, our controller layer and model layer are separated by some service objects – one of which handles our wiki implementation. To test the wiki controller, we mock out the actual wiki using a SchMock:

class WikiControllerTest < Test::Unit::TestCase

In the setup method we create the mock_wiki and inject it into the application controller from which everything that uses it can access it.

  def setup
    @mock_wiki = SchMock.new
    ApplicationController.wiki = @mock_wiki
    @controller = WikiController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
end

At the end of every test we verify that the mock_wiki has had all its expectations fulfilled.

  def teardown
    @mock_wiki.__verify
  end

Now the real meat of the test. We want to make sure that if we try to edit a non-existing page that we’re redirected to the ‘new’ action. We tell our mock_wiki to expect a call to page_exists? with a parameter ‘nonexistent’ and when it gets it to return false.

I find this simpler than using the built-in fixtures functionality in rails to set up the database to a known state, because the fixture appears right there in the code rather than derived from two-step removed database tables and I don’t have to remember to delete any pre-existing state from the database. It also runs much, much quicker.

  def test_should_redirect_to_new_page_form_if_asked_to_edit_nonexisting_page
     @mock_wiki.__expects(:page_exists?).with('nonexistent').returns(false)
     get :edit, { :page => 'nonexistent'}
     assert_redirected_to :action => 'new', :page => 'nonexistent'
  end
end

I think my life-expectancy has been greatly improved through the use of mock-objects and I thank the good chaps who wrote and promoted the original mock-objects – people like Steve Freeman, Nat Price, Ivan Moore and Joe Walnes – who write much more elegantly than me on the topic.

Tags: , ,

Leave a Comment