Mock Commands, Stub Queries
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 Cardef initialize(parts = []) @parts = parts enddef start started = true @parts.each do | part | # commenting out for failure # started = started && part.start endstarted endend
class SomeTest < Test::Unit::TestCasedef test_start engine_mock = mock("engine_mock") car = Car.new([engine_mock])engine_mock.expects(:start).returns(false) assert !car.start endend
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::TestCasedef test_should_start_engine engine = mock('engine') car = Car.new([engine])engine.expects(:start)car.start enddef test_should_start_if_engine_starts engine = stub('engine') car = Car.new([engine])engine.stubs(:start).returns(false)assert !car.start endend
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 Cardef initialize(parts = []) @parts = parts enddef start @parts.each { |part| part.start } enddef started? @parts.all? { |part| part.started? } endend
class SomeOtherTest < Test::Unit::TestCasedef test_should_start_engine engine = mock('engine') car = Car.new([engine])engine.expects(:start)car.start enddef test_should_not_be_started_if_engine_is_started engine = stub('engine') car = Car.new([engine])engine.stubs(:started?).returns(false)assert !car.started? endend
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.