When the double must actually work
Stubs answer with canned values, and that is enough when the unit asks its dependency one question. But many units hold a conversation with a dependency: save something, read it back, update it, read it again. A stub cannot keep that story consistent — if load always returns the same canned answer, there is no way to test "rename the user, then read back the new name".
This is the job of the third double from lesson 5-1, the fake: a genuinely working implementation of the dependency, just far lighter than the real one. The classic fake is an in-memory dictionary standing in for a database: save writes to the dict, load reads from it, and the whole "database" vanishes when the test ends. Working teams lean on fakes constantly, because swapping a production database for an in-memory store turns a seconds-long test needing setup and cleanup into a milliseconds-long one needing neither.
Code exercise · python
Run this. FakeUserStore is a small class (classes are from Advanced Python) whose save and load are backed by a plain dictionary. Because the fake genuinely works, the test can tell a full story: save Ada, rename her, read back the new name, and confirm that renaming a missing user raises ValueError.
Choosing among the three
The selector question is what your test needs from the dependency:
- needs a fixed answer → stub
- needs its interactions recorded → mock
- needs to keep working across several calls → fake
They combine freely: one test may stub the clock, mock the email sender, and fake the database, all at once. What never changes is the goal from lesson 5-1 — the unit's real logic runs, and every neighbor is under your control.
Quiz
You are testing a shopping-cart module: the code adds three items to cart storage, then computes a total by reading the cart back. Why is a stub the wrong double for the storage?
Code exercise · python
Your turn. FakeCounterStore is written for you. Test record_visit with three asserts telling one continuous story: the first visit to "/home" returns 1, the second returns 2, and a first visit to "/about" still returns 1 because pages count independently. Then print: 3 tests passed against the fake