A test double is a test object or a test function, that looks and behaves like its production counterpart, but is actually a simplified version that reduces the complexity and enables simpler testing. One can represent all types of test double as an inheritance tree like this:
Where Double
is an abstract test double, which has no functionality - it is a general concept to talk about test doubles.
Dummy
- is a test double, that is used to fill parameter lists, in cases where these parameters are not used by production code. Simplest Dummy
would look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Stub
- is a test dummy, additionally, providing an indirect input for the production code from the test. “Indirect” means here via a method call on the stub object or a call of the stub function. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Spy
- is a test stub, additionally, verifying an indirect output of the production code, by asserting afterward, without having defined the expectations before the production code is executed. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
Mock
- is a stub, but the expectations are defined before the execution of the production code and it can verify itself after the execution. A simple example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
Mocks can be much more complex (verifying order of messages, allowing multiple messages to be sent, etc.). So it is recommended to either:
- avoid them and use simpler test doubles, or
- use a full-blown well-tested mocking framework.
And if you do have to use your own custom mocks, please, write tests for them, since they can have a lot of logic inside of them.
And, finally, Fake
- is a test double providing a simpler implementation used in the tests instead of the real thing. A good example is an in-memory database gateway, that behaves the same way the real one would, but it stores all the data in the memory. A very simple example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Obviously, fakes require full-blown testing for them. And if the real implementation is testable (even if it is slow), it is a good idea to have the same test suite for both: fake and real implementation. This way we can really be sure, that the fake behaves the same way as the real thing. And don’t forget about the edge cases, for example, if the real thing can throw a ConnectionError
, the fake should be able too (after being instructed to do so via a special method in the tests).
Thanks
Thank you for reading, my dear reader. If you liked it, please share this article on social networks and follow me on twitter: @tdd_fellow.
If you have any questions or feedback for me, don’t hesitate to reach me out on Twitter: @tdd_fellow.