Mocks, stubs, doubles, fakes, dummies, spies… What are they, and when should you use them?
should I use them?
Context
OO is all about message passing. An object sends messages to its
collaborators, whose methods respond to these messages by doing things and/or
returning values. The important parts of the design are how objects interact
by sending messages to each other, not the data they hold internally.
When test-driving a design from the outside-in, we start with a high-level
integration test against the external behaviour of the system, then work our
way in, discovering the required collaborating roles as we go ("programming
by wishful thinking"). But how do we test which messages the class we're
writing sends to its collaborators, when they don't yet exist?
Undurprisingly, with mocks, stubs ...
Terminology (from xUnit Test Patterns)
Test double - generic term for anything used in tests instead of real object
Dummy - no behaviour (eg when we have to pass in a parameter we aren't
going to use)
Fake - a working, but not production, implementation (eg in-memory
database)
Stub - provides canned responses to messages, and may store call information
Mock - expects specific messages, and fails tests if it doesn't receive
them (or receives others)
Spy - like a stub, but can be interrogated about the messages it received
Classification of objects and messages
Values and objects (terminology from GOOS)
Values are pieces of data with no behaviour, eg name, location or address.
Should be treated as immutable.
Objects have identity, state and behaviour.
Nouns and verbs
Just to add to the confusion, "stub" and "mock" are both nouns and verbs.
And, especially in a dynamic language like Ruby, you can mock a call to a
stub or stub a call to a mock. In fact, in rSpec you get an instance of the
same class whether you ask for a stub or a mock. You can also ask for a
"double", which may or may not help reduce the confusion.
Also - and again especially in dynamic languages - you can stub or mock
methods on real instances of a class.
Queries and commands
A query is a request for some object or piece of data, which should have no
side-effects.
A command is a request for the receiver to take some action. It may or may
not also return something.
The "tell don't ask" principle of OO design says that we should prefer to
tell an object to do something (command), rather than ask it for some data
(query) and perform an operation ourselves on the thing that's returned.
This helps maintain encapsulation. Obviously this isn't universal -
sometimes we do need to ask as well.
Usage
Mocks sometimes get a bad name, when people overuse them and find they make
tests too brittle and closely-tied to the implementation.
Queries
Calling methods to get data, not because you want to do something.
Antipattern: mock to check a query method is called (worse if you don't
check return!)
Test the behaviour that depends on the returned value. Stub the query to
return appropriate values.
If arguments to query are important, stub the method to specifically
respond to the correct arguments.
Commands
Calling methods because you want collaborators to do something.
Replace collaborator with mock object, and assert that the expected
messages are received.
Think of the collaborator you're mocking as a role (or interface), not an
instance of a specific class. The class under test shouldn't need to know
exactly what kind of object it's sending messages to, just that it's
something that can respond to those messages.
Values
Antipattern: replace value objects with stubs
Just use real objects. They don't have any behaviour to stub.
Your own methods
Don't stub or mock calls to your class's own methods (public, private or
superclass). That way lies madness.
Just test the class using its external interface. Private methods are an
implementation detail that should be able to be refactored without breaking
any tests.
Other people's code
Mock objects are often introduced as a means of testing interactions with
third-party code. This is unfortunate as they were initially introduced as a
design technique, and you can't use mocking to drive the design of code you
don't control.
In general it's better to wrap external APIs with a thin layer which can be
integration-tested with the real API. This gives you an interface that you
can control, and you can drive its design by mocking calls from the code that
needs to use it.
Summary
Stub queries; mock commands
Don't stub value objects
Recognise when to stop mocking and test behaviour by asserting state
Don't stub or mock internal calls
Don't mock classes you don't own
Listen to the tests! Mocks are there to help drive the design - if you're
having to stub lots of calls, think about how you could refactor to reduce
coupling.