Seams & Test Doubles
intermediate14 min readLesson 91 of 180
Stubs, fakes, spies, mocks — and extracting Clock-like seams so time and randomness stop leaking into tests.
Seams and test doubles
A seam is a replaceable point in code — usually a constructor-injected interface. Doubles plug into seams:
- Stub — returns canned data (
FakeGatewayfrom Module 2) - Fake — a working lightweight implementation (in-memory repo)
- Spy — records calls for later verification
- Mock — pre-programmed expectations (use sparingly; mocks that know too much make refactoring painful)
interface Clock { Instant now(); }
class SubscriptionService {
private final Clock clock;
SubscriptionService(Clock clock) { this.clock = clock; }
boolean isExpired(Instant until) { return clock.now().isAfter(until); }
}
// test: freeze time instead of sleeping/_waiting
Clock frozen = () -> Instant.parse("2026-09-14T00:00:00Z");
The rule: if code calls Instant.now() or Math.random() deep inside,
it has no seam — extract the decision behind an interface. Deterministic
tests are the payoff.