The Difference between Mocks and Stubs
Some recent converstations have made me realise that a lot of people are confused by the difference between mocking and stubbing. I’ve realised that I’ve only added to the confusion by calling part of the Mocha library, Stubba.
The difference between mocking and stubbing
Stubbing a method is all about replacing the method with code that returns a specified result (or perhaps raises a specified exception). Mocking a method is all about asserting that a method has been called (perhaps with particular parameters).
If you think about it, it’s difficult (or impossible?) to do mocking without stubbing – you need to return from the mocked method, so that the code under test can complete execution. So mocking libraries tend to implicitly or explicitly allow you to do stubbing.
Mocking and stubbing with Mocha
The difference between Mocha and Stubba
Mocha is a traditional mocking library very much in the JMock mould. Stubba is a separate part of Mocha that allows mocking and stubbing of methods on real (non-mock) classes. It works by moving the method of interest to one side, adding a new stubbed version of the method which delegates to a traditional mock object. You can use this mock object to set up stubbed return values or set up expectations of methods to be called. After the test completes the stubbed version of the method is removed and replaced by the original.
I’m thinking of ditching the name Stubba, because this part of the library is not solely concerned with stubbing. Let me know what you think.
Martin Fowler has a must-read article on why Mocks Aren’t Stubs.
Another great reference is JMocks documentation – in particular Yoga for your Unit Tests.
class ClassUnderTest def initialize(dependency) @dependency = dependency end def do_it @dependency.execute end end
class Dependency def execute # complicated code end end
class MyTest < Test::Unit::TestCase class DependencyStub def execute true end end def test_stubbing_example dependency = DependencyStub.new class_under_test = ClassUnderTest.new(dependency) assert_equal true, class_under_test.do_it end end
class MyTest < Test::Unit::TestCase class DependencyMock attr_reader :call_count def initialize @call_count = 0 end def execute @call_count += 1 end end def test_mocking_example dependency = DependencyMock.new class_under_test = ClassUnderTest.new(dependency) class_under_test.do_it assert_equal 1, dependency.call_count end end