Test Double
Pattern: A named solution to a recurring problem.
Replace a slow, unsafe, or unavailable dependency with the lightest substitute that preserves the behavior your test needs to prove.
If you’ve reviewed a test that creates six mocks before calling one method, you’ve met the danger this pattern is meant to contain. A test double is the broad name for any object that stands in for a production dependency during a test. The five common kinds aren’t interchangeable: each removes a different obstacle and makes a different promise.
Understand This First
• Test — the executable claim whose scope the double narrows.
• Boundary — the seam where a production dependency can be replaced without copying internal design.
• Test Oracle — the check that decides whether behavior is correct after the double has supplied or recorded an interaction.
Context
Tests often reach beyond the code they mean to examine. A payment service calls a bank, an order handler writes to a database, and a notification worker sends email. Running those dependencies in every unit test can make the suite slow, nondeterministic, expensive, or dangerous.
A test double replaces one such dependency inside the Fixture. The substitution belongs at the test’s boundary, not between every pair of internal classes. This is a tactical testing pattern: it buys isolation for one test while preserving enough real behavior elsewhere to keep the suite honest.
Problem
You need a focused test, but the code under test depends on something you can’t or shouldn’t run. Calling the real dependency may send money, mutate production data, require a network service, or add seconds to every case.
Replacing that dependency can solve the immediate problem and create a subtler one. If the substitute behaves differently from the real system, the test proves only that the code works with its own fiction. How do you isolate the boundary without hollowing out the behavior you meant to test?
Forces
• The test should run quickly and deterministically, but the real dependency may be slow or variable.
• Some boundaries are unsafe or costly to exercise on every run.
• A substitute is easier to control than a real service, but every invented behavior can drift from reality.
• Interaction checks can describe an important protocol, but they can also freeze incidental call order and private structure.
• Coding agents can generate doubles cheaply, which makes unnecessary isolation easy to miss in review.
Solution
Choose the lightest test double that removes the obstacle, and place it at a real boundary. Name the kind in the test or review; the label tells reviewers what evidence to expect:
• A dummy fills a required parameter but is never used. Pass a placeholder logger when the exercised path doesn’t write logs.
• A fake has a working, simplified implementation. An in-memory repository stores and retrieves records without running a database server.
• A stub returns canned answers. A shipping-rate client returns a known quote so the test can exercise price calculation.
• A spy records what happened for later inspection. A mail sender records recipients and message bodies while doing no delivery.
• A mock carries expectations about the calls it should receive. It fails when the code doesn’t make the expected interaction.
Prefer state and output checks over interaction checks when either can prove the same behavior. A fake often preserves more useful behavior than a nest of mocks. A stub is enough when the test needs one fixed answer. Use a mock when the interaction itself is the contract, such as requiring a transaction to roll back or an audit event to be emitted exactly once.
Keep every double accountable to the dependency it represents. Run a smaller set of integration tests against the real boundary. For remote APIs, Consumer-Driven Contract Testing can verify that canned requests and responses still match the provider. For internal fakes, run the same behavioral test suite against both the fake and the production implementation where practical.
Tell coding agents the project’s policy before they write tests. An Instruction File can name the boundaries that may be doubled, prefer fakes or stubs over interaction-heavy mocks, and require real integration coverage for each doubled external system. During review, ask which failure the double exposes and which real behavior it removes.
How It Plays Out
An agent is asked to add optimistic locking to an order repository. It generates a unit test that mocks the repository interface itself, programs save() to succeed, and asserts that save() was called. The test stays green when the implementation drops the version check because the mocked boundary is the behavior under test. The reviewer deletes that mock, runs the repository against a disposable database, and asserts that a stale update is rejected. The slower test now proves the feature.
A checkout service needs fast tests for reservation order. Its first suite uses a mock store with a chain of expected calls, so harmless refactoring breaks dozens of tests. The team replaces the mock with a small in-memory fake that implements the same repository contract as production. A test then exposes a real bug: cancelling two reservations in reverse order leaves the first item allocated. The mock had prescribed the happy call sequence; the fake preserved enough state for the bad sequence to matter.
Research on agent-authored tests gives reviewers a reason to look closely. A 2026 study of 1,254,878 commits across 2,168 repositories found that 36% of agent test commits added mocks, compared with 26% of non-agent test commits. The authors also found less use of spies and fakes in repositories with agent mock activity. In a manual check of 100 detector-flagged commits from 10 repositories, 94 were true positives, so the exact percentages aren’t a universal law. The study suggests a bias toward the easiest double to generate in this 2025 corpus; other ecosystems and later agent versions may differ.
Consequences
Benefits. The right double makes a focused test fast, safe, and repeatable. The taxonomy sharpens review. Instead of “this mock is wrong,” a reviewer can say the test needs a stub, a stateful fake, a spy, or no double. Doubles can also expose a cleaner boundary when production code has tangled external work into its core logic.
Liabilities. Every double creates another implementation that can disagree with production. Interaction-heavy mocks couple tests to call order and private structure, so valid refactoring breaks them. A large unit suite can stay green while real integrations fail. Contract checks, a thin layer of real-boundary tests, and occasional Mutation Testing reduce that risk, but they don’t erase it.
Isolation is a budget, not a goal. Spend it where the real dependency makes focused testing impractical, then restore reality at the next test layer.
Related Articles
Depends on: Boundary — A useful test double replaces a dependency at a real boundary rather than imitating the internals of the code under test.
Depends on: Test — A test is the executable claim whose scope the double narrows.
Depends on: Test Oracle — A double controls a dependency, but the oracle still decides whether the observed result is correct.
Supported by: Instruction File — An instruction file can tell coding agents which boundaries may be doubled and which kinds of doubles the project prefers.
Tested by: Mutation Testing — Mutation testing can reveal a double that bypasses the behavior the suite claims to protect.
Uses: Consumer-Driven Contract Testing — Provider verification keeps a consumer's stubbed view of an external service aligned with the service's real contract.
Uses: Fixture — A test double is often created and configured as part of the test's fixture.
Sources
• Gerard Meszaros developed the dummy, fake, stub, spy, and mock taxonomy in xUnit Test Patterns (2007).
• Martin Fowler’s Test Double (2006) introduced Meszaros’s vocabulary to a wider software audience and explains why “mock” shouldn’t be the umbrella term.
• Andre Hora and Romain Robbes measured agent and non-agent mocking behavior in Are Coding Agents Generating Over-Mocked Tests? An Empirical Study (MSR 2026), including the corpus, taxonomy distribution, and detector limits cited here.
• Google’s Testing Blog article Don’t Overuse Mocks argues for testing real implementations when practical and reserving mocks for boundaries where they earn their maintenance cost.