intermediate
Mocks versus fakes
Use mocks for interaction checks, fakes for realistic lightweight behavior, and avoid doubles that hide broken contracts.
Test doubles replace collaborators. Mocks (via libraries or hand-rolled spies) verify interactions—was `sendEmail` called with the right args? Fakes provide working lightweight implementations—an in-memory repository with real insert/select semantics.
Use mocks when the contract is "call this port"; use fakes when behavior and state matter. Stubs return canned values; spies record calls without strict expectations.
On interviews: refactor a test from over-mocked to fake repository and explain which failures each reveals.
Common pitfalls: mocks that pass when production code never calls the port, fakes that diverge from real DB semantics, and asserting implementation order instead of outcomes.
The trade-off is interaction precision versus behavioral realism—heavy mocking speeds tests but hides integration bugs.
Checklist:
- Pick double type by what you need to prove.
- Prefer fakes for persistence and clocks when behavior matters.
- Reset doubles between tests.