A test double is any object that stands in for a real dependency during a test, the general term for the whole category. Mock gets used as a catch-all for this entire category in everyday conversation, which is convenient in casual speech and genuinely confusing in a design discussion, because the five specific kinds of test double behave differently and prove different things. The taxonomy comes from Gerard Meszaros and was popularized by Martin Fowler: dummy, stub, spy, fake, and mock.
What changed in 2026
- The vocabulary spread beyond testing specialists. More style guides and team documentation now define these five terms explicitly, since "just use a mock" stopped being precise enough once teams started debugging why mock-heavy suites were brittle.
- Fakes gained ground relative to mocks for testing anything with real logic worth exercising, like an in-memory repository standing in for a database, as teams pushed back against long chains of mock expectations.
- Static typing made doubles safer to maintain, with generated doubles from interfaces catching a mismatch between a double and the real thing at compile time in many statically typed languages.
The five kinds of test double
| Kind |
What it does |
Example |
| Dummy |
Does nothing; exists only to satisfy a required parameter |
An empty object passed to a constructor that requires a logger but never uses it in this test |
| Stub |
Returns fixed, canned data when called |
A user repository stub that always returns the same test user |
| Spy |
Records how it was called, checked after the fact |
A stub that also remembers how many times it was called, then the test inspects that record |
| Fake |
A real, working, simplified implementation |
An in-memory list standing in for a database table |
| Mock |
Pre-programmed with expectations, verified during the test |
An object that fails the test itself if it is not called with the exact expected arguments |
The rows are ordered roughly by how much real behavior the double has, from none in a dummy to a full simplified implementation in a fake, with mock sitting apart from the other four because it actively asserts, rather than passively standing in.
Why the distinction matters in practice
A stub and a mock can be built from the exact same mocking library and look nearly identical in code. The difference is what the test checks. If the test inspects the return value your code produced using the double, the double acted as a stub. If the test inspects whether the double was called correctly, the double acted as a mock. For a deeper look at that specific practice, see what mocking in tests actually means.
When each kind earns its place
- Dummy — only when a function signature requires an argument the test genuinely does not care about.
- Stub — when your code needs data from a dependency but the test is not about that dependency, like feeding a fixed exchange rate into a currency conversion test.
- Spy — when you need to confirm a side effect happened, like an analytics event being sent, without failing the whole test the moment the call looks slightly different.
- Fake — when the real behavior of the dependency matters enough that canned data would miss real bugs, and a full real instance is too slow or heavy for a unit test.
- Mock — when the entire point of the test is confirming an interaction took place, like verifying a refund API was called exactly once with the correct order ID.
The most common mistake
Reaching for a mock by default, for everything, regardless of what the test is actually trying to prove. A mock is the most rigid of the five: it fails the test the moment the interaction does not match its expectations exactly, including on harmless refactors that do not change behavior. A stub or a fake is often the less brittle, more honest choice when you are not actually testing an interaction.
FAQ
Is a mock the same as a test double?
No. A test double is the general category; a mock is one of five specific kinds within it, alongside dummy, stub, spy, and fake.
What is the difference between a stub and a mock?
A stub returns data and is not itself checked; the test verifies the output your code produced using that data. A mock is checked directly; the test verifies the mock was called the way it should have been.
Is a fake the same as the real implementation?
No, but it behaves like one. A fake has real, working logic, an in-memory database instead of a real one, for example, just simplified enough to be fast and safe inside a test.
Do I need a mocking library to create any of these?
No. All five can be hand-written as plain objects or classes. Mocking libraries exist to remove the repetitive boilerplate, especially for mocks and spies, not because the concepts require special tooling.
Where to go next