Test Double

An umbrella term (coined by Gerard Meszaros) for any object that stands in for a real production dependency during a test — analogous to a film stunt double standing in for an actor.

Problem

A unit under test often depends on collaborators that are slow (databases, network), non-deterministic (clocks, random number generators), hard to set up (third-party services), or destructive (payment processors). Testing with real collaborators makes tests slow, unreliable, or impossible to run in CI.

Solution / Explanation

Meszaros defined five distinct types of test double. Fowler documented them on martinfowler.com. Each type has a specific role:

The Five Types

TypeHas Working Implementation?Records Interactions?Verifies Expectations?
DummyNoNoNo
FakeYes (simplified)NoNo
StubNo (canned answers)NoNo
SpyNo (canned answers)YesOptional
MockNoYesYes (mandatory)

Dummy Passed to satisfy a parameter signature but never called during the test. Example: passing null or an empty object for a logger that isn’t exercised in the test path.

Fake Has a real but simplified implementation that works correctly but is unsuitable for production. Classic example: an in-memory repository that replaces a SQL database — full CRUD semantics, no disk I/O. Fast, deterministic, useful for integration tests.

Stub Returns hardcoded, pre-programmed answers to specific calls. Does not respond to calls outside what the test programs. Example: priceService.getPrice("AAPL") always returns 150.00. Used to control inputs to the unit under test.

Spy A stub that also records how it was called (which methods, with what arguments). The test can then assert post-hoc: “the email service was called once with the correct address.” Less prescriptive than mocks — the test asserts after the fact rather than setting expectations up front.

Mock Pre-programmed with explicit expectations about which calls it must receive. Throws an exception if an unexpected call arrives or if an expected call never happens. Verification is built in. Encourages specifying exact interaction protocols.

Mock vs. Stub — The Key Distinction

Fowler emphasises this is the most confused pair:

  • A stub is about state verification: set up the collaborator, run the unit, assert the unit’s output or state.
  • A mock is about behaviour verification: assert that the unit called the collaborator in the right way.

Overusing mocks leads to tests that are tightly coupled to implementation details and break on every internal refactor.

When to Use

ScenarioPreferred Double
Satisfying unused parameterDummy
Fast in-memory replacementFake
Controlling return values / side effectsStub
Asserting a call was made (post-hoc)Spy
Asserting exact interaction protocolMock

Trade-offs

BenefitCost
Eliminates slow/unreliable external dependenciesOver-mocking hides real integration bugs
Enables deterministic, fast testsDoubles must be kept in sync with real interfaces
Isolates the unit under testMock-heavy tests couple tests to implementation