Mocking & Stubbing

Test doubles stand in for a unit's real dependencies so you can test it in isolation — feeding it controlled inputs and observing its interactions without hitting a network, database, or clock. Used well, they make tests fast, deterministic, and focused. Used poorly, they make tests brittle and meaningless.

The single most important rule: mock at boundaries — external APIs, databases, time, randomness — and let your own logic run for real. When tests mock internal implementation, they stop verifying behavior and start verifying that you called your own methods, which breaks on every refactor and proves nothing.

TL;DR

Quick Example

A stub feeds canned data; the real logic runs against it:

Types of Test Doubles

When to mock

When NOT to mock

Mocking in Practice

JavaScript (Jest)

Python (unittest.mock)

Mocking Time

Time is the classic non-deterministic dependency — freeze it:

Common Patterns

Partial mocking — fake one method, run the rest

Fake — a working, simplified implementation

Best Practices

Common Mistakes

Mocking internal implementation

Over-mocking until the test proves nothing

FAQ

What's the difference between a mock and a stub?

A stub provides canned return values — it controls what the unit receives. A mock additionally asserts that specific interactions happened — it verifies what the unit does. Use a stub when you just need to feed data in; use a mock when the interaction itself (e.g. "an email was sent") is the behavior under test. Conflating the two leads to tests that over-specify implementation.

Why is "mock at boundaries" the golden rule?

Because mocking your own internals makes tests verify how code works rather than what it does — so any refactor breaks them even when behavior is unchanged, and the tests can pass while the real logic is broken. Mocking only the boundaries (network, DB, time) keeps your actual logic under test while removing the slow, flaky, non-deterministic parts.

When should I use a fake instead of a mock?

Use a fake (a working, simplified implementation — like an in-memory repository) when a dependency is stateful and used across many tests. Fakes let real interactions happen against simple storage, producing tests that read naturally and don't break on refactors. Reach for mocks when you specifically need to verify an interaction occurred; use fakes when you need believable behavior.

How do I test code that depends on the current time or randomness?

Make those dependencies controllable. For time, freeze the clock (jest.setSystemTime, freezegun) or inject a clock object. For randomness, inject a seeded or fixed source. This removes non-determinism so the test produces the same result every run — and it's the same dependency-injection design that makes code testable in the first place. See Unit Testing.

Related Topics

References