Skip to main content
๐Ÿ“œ WAYPOINT LESSON

Seams & Test Doubles

โญโญ intermediateโณ 14 min read๐Ÿ“ Lesson 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 (FakeGateway from 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.