Contents

Engineering Craft › Testing

Test Double

Any stand-in for a real dependency in tests.

Also known as: test doubles, mocks stubs fakes, stub vs mock, dummy stub spy mock fake

A test double is any stand-in for a real dependency used during testing, like a stunt double in a film. It lets you test a piece of code without the slow, unpredictable or side-effect-prone real thing (a payment API, a clock, a database, an email service). The term covers several kinds, often loosely called “mocks”:

KindWhat it doesExample
DummyFills a parameter and is never actually usedNone passed where an argument is required
StubReturns canned answersA payment gateway that always returns “approved”
SpyA stub that also records how it was calledCounts calls to send_email
MockPre-programmed with expectations that it verifies“Expect charge(500) to be called once”
FakeA working simplified implementationAn in-memory repository instead of a database
class StubGateway:                       # stub: fixed response
    def charge(self, amount): return {"status": "approved"}

class FakeUserRepo:                      # fake: real behavior, simple storage
    def __init__(self): self.users = {}
    def save(self, u): self.users[u.id] = u
    def find(self, uid): return self.users.get(uid)

def test_checkout_approves_order():
    service = Checkout(gateway=StubGateway(), users=FakeUserRepo())
    assert service.pay(order).status == "paid"

Choosing

  • Use a stub when you only need a controlled input (“what if the API fails?”).
  • Use a fake when behavior matters and the real thing is heavy.
  • Use a mock or spy when the interaction is the behavior you’re verifying (an email was sent).

Guidelines

  • To use doubles, code needs seams where dependencies can be swapped. That’s a reason to pass dependencies in (dependency injection).
  • Double the boundaries (network, time, randomness, third parties), not your own internal logic.
  • Overuse makes tests brittle and meaningless: tests that mirror the implementation break on every refactor, and can pass while production fails (over-mocking).
  • Keep some tests that use the real dependency (integration tests).
  • A double can drift from the real thing’s behavior. Check them against each other (for example with contract tests).