The Story of Mocks — From Endo-Testing to Mockito
In 2000, a paper by Mackinnon, Freeman, and Craig proposed the concept of the 'mock object,' giving birth to the testing philosophy later known as the London school. We trace the lineage through GOOS, jMock, Mockito, and Sinon.js, Meszaros's five-way classification of test doubles, and the pitfalls of over-mocking.
Practitioners of TDD (Article 4) ran into a wall early on: how do you write a unit test for an object that depends on a database, a network, or some other object? Connecting to a real database makes tests slow and unstable; calling a real external API can trigger charges or side effects. But simply replacing the dependency with a “fake that returns nothing” doesn’t let you verify that the object under test is actually using that dependency correctly. In 2000, one paper gave a clear answer to this problem.
The Endo-Testing Paper — XP2000
In 2000, at the XP2000 conference held on the island of Sardinia, Italy, Tim Mackinnon, Steve Freeman, and Philip Craig presented a paper titled “Endo-Testing: Unit Testing with Mock Objects.” It is considered the first systematic presentation to put the concept of the mock object before the world.
The “Endo” (inner) in the title refers to the fact that a mock object is injected inside the domain code, where its behavior is observed from within. The argument was simple but powerful:
- Replace the collaborators that the object under test depends on with fakes, instead of the real thing
- But rather than merely returning fixed values, verify that the expected calls actually happened — this was the crucial difference from a mere “stub”
- With mocks, you can write fast, deterministic unit tests even for code with external dependencies
- In the pursuit of “design that is easy to test,” a style naturally emerges in which dependencies are explicitly injected into objects (dependency injection)
The paper wasn’t just a proposal for a testing technique — it was received as reinforcing the TDD idea that “writing tests itself drives good design.”
The Birth of the London School
The paper’s authors went on to develop this practice further, using mock objects aggressively and organizing design around the “collaboration” and “conversation” between objects. This became the origin of what is now called the London school, or mockist style of testing.
Its characteristics:
- The messages (method calls) sent by the system under test (SUT) are themselves the object of verification — behaviour verification
- Design proceeds “outside-in” — first you test the system’s outer interface, define as mocks the collaborators needed to make that test pass, and only then fill in the implementation
- The division of responsibilities and interface design between objects is itself folded into the TDD feedback loop
GOOS — Growing Software While Testing It (2009)
The book that systematized the thinking of the London school was Steve Freeman and Nat Pryce’s Growing Object-Oriented Software, Guided by Tests (known as GOOS, 2009).
Rather than stopping at unit tests for a single class, this book showed, through the example of building a GUI application (an auction-sniping client), how to grow an entire system by driving it with tests from the “outside” — the behavior visible to the user. Concepts it popularized include:
- Walking Skeleton: build a minimal end-to-end skeleton first, then flesh it out
- A thorough commitment to “Tell, Don’t Ask” and collaborative design between objects
- The idea that a mock is a tool for “writing the specification of a collaborator that doesn’t exist yet”
Establishing the Vocabulary of “Test Double”
Words like “mock,” “stub,” and “fake” were originally used inconsistently from developer to developer. In 2004, Gerard Meszaros, in a paper presented at the pattern-language conference PLoP 2004, first proposed the umbrella term “Test Double” to cover them all. “Double” is a metaphor borrowed from film production’s “stunt double” — capturing the image of “an actor standing in for the real thing.”
The term was formally systematized in his 2007 book xUnit Test Patterns: Refactoring Test Code, and its five-way classification became industry-standard vocabulary.
| Kind | Role | Nature of verification |
|---|---|---|
| Dummy | Passed only to fill a parameter slot; never actually used | None |
| Stub | Returns a predetermined value | None (supports state verification) |
| Spy | Records calls so they can be checked afterward | Leans toward state verification |
| Mock | Expectations for calls are set up in advance and verified during or after execution | Behaviour verification |
| Fake | A lightweight but realistic implementation (e.g., an in-memory database) | State verification |
What makes this classification important is that it clarified something that had gotten blurred: “mock” is, strictly speaking, only one kind of test double (the one that does behaviour verification), even though in everyday speech it’s often used loosely to mean “test doubles in general.”
The Lineage of jMock, EasyMock, and Mockito
The practice of mock objects took concrete shape as a family of Java mocking libraries.
| Library | Characteristics |
|---|---|
| jMock | The Endo-Testing paper’s authors were involved in its development. An API with strong London-school character, emphasizing strict, up-front declaration of expectations |
| EasyMock | Generates mocks via a two-phase “Record & Replay” API style. Became more widely adopted than jMock |
| Mockito | Developed starting in 2008, centered on Szczepan Faber, in the course of system development at the London newspaper The Guardian. Began on top of EasyMock’s codebase |
Mockito is characterized by a readable API in the style of when(...).thenReturn(...), and by lenient verification by default (it works without strictly specifying call counts), which eased the constraint of jMock/EasyMock’s requirement to “declare all expectations up front.” Thanks to this ease of use, Mockito established itself from the 2010s onward as the de facto standard mocking library in the Java world.
// Mockito: you can both configure a stub and verify behaviour
PaymentGateway gateway = mock(PaymentGateway.class);
when(gateway.charge(1000)).thenReturn(true);
OrderService service = new OrderService(gateway);
service.checkout(order);
verify(gateway).charge(1000); // verify after the fact that the call happened
By contrast, jMock-style APIs declare expectations before the call happens.
// jMock: declare expectations first, then execute
context.checking(new Expectations() {{
oneOf(gateway).charge(1000); will(returnValue(true));
}});
service.checkout(order); // fails immediately if a call violates the expectation
This difference may look trivial, but it reflects a design-philosophy divide: how easy is it, reading the test, to tell what is actually being verified?
Test Doubles in the JavaScript World
In the JavaScript world, Sinon.js has played a similar role, widely used as a library that provides spies, stubs, mocks, and fake timers under a unified API.
// Sinon.js: combining a stub and a spy
const stub = sinon.stub(gateway, "charge").returns(true);
service.checkout(order);
assert(stub.calledWith(1000)); // confirm, spy-style, what the call looked like
In today’s JavaScript ecosystem, Jest and Vitest ship with their own built-in mocking mechanisms (jest.fn()/vi.fn()), so you can work with test doubles without adding a standalone mocking library at all. This, too, is a sign that the field has matured enough to be folded into language- and framework-level standard features.
Mockist vs. Classicist, Revisited
There are broadly two schools of TDD practice.
- London school (mockist): replace collaborators with mocks as a rule, and verify the messages (behaviour) sent by the SUT. The lineage of Mackinnon, Freeman, Pryce, and colleagues.
- Detroit school (classicist): the classical position closer to Kent Beck — use real objects wherever possible, and verify the final state (return values or object state), i.e., state verification. Mocks are reserved for cases where using the real thing is genuinely difficult (external APIs, the clock, randomness, and so on).
This split is rooted in a design-philosophy disagreement over how much a test should know about implementation details, and it isn’t merely a matter of taste — it feeds directly into trade-offs around refactoring resilience, clarity of test intent, and execution speed.
The Cost Called Over-Mocking
As mocks spread, a problem came to be widely recognized: over-mocking.
- Replacing every collaborator with a mock tends to turn tests into verifications of implementation details — which method is called in what order — so tests break with every refactor
- The setup for interactions between mocks grows complex, and the readability and maintainability of the test code itself suffers
- If the assumption that “the mock behaves like the real thing” breaks down, tests pass while production breaks — a false negative
- Overusing mocks at the unit-test level leads to the classic failure pattern where “all the tests pass, but the system doesn’t work once wired together”
Out of this reflection, a compromise principle is now widely shared in practice: use mocks only at boundaries you don’t own (external services, the clock, the filesystem, and the like), and use the real thing or a fake for collaborators your own team owns. This design decision is closely tied to questions of testable design and interface decomposition (Article 12).
From This Article to the Next
Mocks were a technique for verifying specific behavior — whether a particular call happened correctly. What we turn to next is the opposite idea entirely. Instead of writing individual input-output examples, you declare “a property that should hold for any input,” and have a tool verify it against a huge volume of randomly generated inputs — property-based testing. We start the story with QuickCheck, born in the Haskell world in 2000.
References
- Tim Mackinnon, Steve Freeman, Philip Craig, “Endo-Testing: Unit Testing with Mock Objects” (XP2000)
- Steve Freeman, Nat Pryce, Growing Object-Oriented Software, Guided by Tests (2009)
- Gerard Meszaros, xUnit Test Patterns: Refactoring Test Code (2007)
- Mockito official documentation (site.mockito.org)
- Sinon.js official documentation (sinonjs.org)