Mock Commands, Stub Queries

Posted by James Mead Fri, 03 Aug 2007 10:10:00 GMT

Zach Moazeni has just posted a suggested patch for Mocha over on his blog. My understanding of the patch is that it means expectations are verified even when an assertion error occurs in the test. Here is his example…


  class Car

    def initialize(parts = [])
      @parts = parts
    end

    def start
      started = true
      @parts.each do | part |
        # commenting out for failure
        # started = started && part.start
      end

      started
    end

  end

  class SomeTest < Test::Unit::TestCase

    def test_start
      engine_mock = mock("engine_mock")
      car = Car.new([engine_mock])

      engine_mock.expects(:start).returns(false)
      assert !car.start
    end

  end

I’ve had a friendly & useful conversation with Zach about it, but I’m not convinced this is the right way to go. Using the one assertion per test school of thought, you can achieve the same goal by splitting the test into two so you get a test failure for the expectation and another for the assertion…


  class SomeTest < Test::Unit::TestCase

    def test_should_start_engine
      engine = mock('engine')
      car = Car.new([engine])

      engine.expects(:start)

      car.start
    end

    def test_should_start_if_engine_starts
      engine = stub('engine')
      car = Car.new([engine])

      engine.stubs(:start).returns(false)

      assert !car.start
    end

  end

Something that makes the example less suitable for mocking is that the Car#start method is both a command and a query. If you separate the two, testing with mocks might be easier…


  class Car

    def initialize(parts = [])
      @parts = parts
    end

    def start
      @parts.each { |part| part.start }
    end

    def started?
      @parts.all? { |part| part.started? }
    end

  end

  class SomeOtherTest < Test::Unit::TestCase

    def test_should_start_engine
      engine = mock('engine')
      car = Car.new([engine])

      engine.expects(:start)

      car.start
    end

    def test_should_not_be_started_if_engine_is_started
      engine = stub('engine')
      car = Car.new([engine])

      engine.stubs(:started?).returns(false)

      assert !car.started?
    end

  end
  

I’d be interested to know what other people think…

One thing I do agree with Zach about is that submitting a suggested patch to an open source project is a great way of initiating a constructive conversation.

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

Mocha 0.3 released with Rails plugin

Posted by James Mead Thu, 24 Aug 2006 17:59:00 GMT

A new version of the Mocha mocking and stubbing library developed at Reevoo has been released.

There is now a Ruby on Rails plugin which can be installed like this…

  $ script/plugin install svn://rubyforge.org/var/svn/mocha/trunk

Here are the release notes…

  • Rails plugin.
  • Auto-verify for all expectations, including those on concrete classes.
  • Include each expectation verification in the test result assertion count.
  • Filter out noise from assertion backtraces.
  • Point assertion backtrace to line where failing expectation was created.
  • New yields method for expectations.
  • Create stubs which stub all method calls.
  • Mocks now respond_to? expected methods.

Thanks for patches from Chris.

Enjoy!

Posted in  | Tags , , , , , , , , , , ,  | 14 comments