Test Doubles
| Double | Purpose | Example |
|---|---|---|
| Dummy | Fill a parameter, never used | null or empty object |
| Stub | Return canned answers | stub.getUser() → User("test") |
| Spy | Record calls for later verification | spy.wasCalled("save") |
| Mock | Verify expected interactions | mock.verify(save, times: 1) |
| Fake | Working implementation, simplified | In-memory database |
Fakes SHOULD be preferred over mocks when possible. Fakes exercise real behavior; mocks only verify expectations. A fake in-memory database catches more bugs than a mock that returns canned SQL results.
You MUST NOT mock what you don't own. Wrap external dependencies (HTTP clients, databases, SDKs) behind your own interface. Mock your interface, not the third-party API directly. This insulates tests from upstream API changes.
Platform tools:
- Python:
unittest.mock(stdlib), pytest-mock - TypeScript/JS:
jest.mock, vitest mocking - Swift: Protocol conformances (no framework needed), swift-testing
- .NET: NSubstitute
- Kotlin: MockK, Turbine for Flow testing