पाठ 10 / 25

Test Doubles: Dummies, Stubs, Fakes, Spies and Mocks

Distinguish the kinds of test doubles and when each is useful.

Stand-ins for collaborators

Unit tests isolate the class under test from slow or unpredictable collaborators: databases, HTTP clients, clocks, message brokers. Test doubles (a term popularised by Gerard Meszaros) replace them. A dummy is passed only to satisfy a parameter and is never used. A stub returns canned answers to calls ("the exchange rate is 83.2"). A fake is a working but simplified implementation, such as an in-memory repository. A spy records how it was called, often wrapping a real object. A mock is pre-programmed with expectations and verifies that specific interactions happened. Mockito creates objects that can act as stubs, spies and mocks. Choose the simplest double that works: fakes often make tests more readable and less brittle than many stubbed calls, especially for repositories. Use mocks with verification for interactions that are the actual behaviour under test, such as "an email is sent when the order is placed", and avoid verifying incidental calls that do not matter to the outcome.

Stand-ins for real collaborators

The class under test talks to doubles instead of real databases, services and clocks.

A central box connected to three dashed-outline boxes that replace solid boxes shown faded in the background.
Figure 4.1 — Replacing collaborators with test doubles.

A fake versus a Mockito stub

Both isolate OrderService from the real database.

// fake: a simple working implementation
class InMemoryOrderRepository implements OrderRepository {
    private final Map<Long, Order> store = new HashMap<>();
    private long nextId = 1;
    public Order save(Order o) { if (o.id() == null) o.assignId(nextId++); store.put(o.id(), o); return o; }
    public Optional<Order> findById(long id) { return Optional.ofNullable(store.get(id)); }
}

@Test
void placesOrderWithFake() {
    var repo = new InMemoryOrderRepository();
    var service = new OrderService(repo, new FixedClock("2026-10-03T10:00:00Z"));
    long id = service.place(cartWith("pen"));
    assertThat(repo.findById(id)).isPresent();
}

// stub: canned answer from Mockito
@Test
void convertsPriceWithStubbedRates() {
    ExchangeRates rates = mock(ExchangeRates.class);
    when(rates.inrPer("USD")).thenReturn(new BigDecimal("83.20"));
    var pricing = new Pricing(rates);
    assertThat(pricing.toInr(new BigDecimal("10"), "USD")).isEqualByComparingTo("832.00");
}

Fakes age better than heavy mocking

A test with ten when(...) lines breaks whenever the implementation reorganises its calls. A small in-memory fake reflects behaviour, not call sequences, and can be reused across many tests.

त्वरित जाँच: Which test double is a simplified working implementation, such as an in-memory repository?

  • Dummy
  • Stub
  • Fake
  • Spy
Answer

Fake — A fake really works, but takes shortcuts unsuitable for production.