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
To stub a method, use the stubs method.
To mock a method, use the expects method.
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
Stubbing example
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
Mocking example
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