🧪

Mocking Strategy & Alternatives (EN)

Testing Intermediate 2 min read 400 words

A lead-level strategy for mocks/fakes/stubs: what to mock, what not to mock, and how to avoid brittle tests

Mocking Strategy & Alternatives (EN)

Mocking is a tool; an overused tool becomes a source of brittle tests. At Tech Lead/Principal level you should be able to explain — and enforce — a consistent policy:

  • what we mock
  • when we prefer fakes
  • when we upgrade to integration/contract tests

1) Definitions (keep teams aligned)

  • Stub: returns canned answers; you don’t usually assert how it was called.
  • Mock: you assert interactions (calls, counts, arguments).
  • Fake: a lightweight working implementation (e.g., in-memory repo).
  • Spy: records calls for later assertions.

2) A practical policy that scales

Mock at the boundary

Mock things that are:

  • slow (DB/network)
  • non-deterministic (clock)
  • expensive (crypto)
  • outside your control (3rd party APIs)

Prefer fakes for “rich” dependencies

If a dependency has a meaningful behavior model, a fake often produces better tests.

Example: a fake repository with simple invariants.

Avoid mocking value objects and domain rules

If it’s yours and deterministic, test it directly.


3) State-based vs interaction-based testing

  • State-based: assert outputs and state changes. More refactor-friendly.
  • Interaction-based: assert collaborator calls. Useful when the interaction is the contract.

When is interaction the contract?

  • “must publish exactly one message”
  • “must call audit logger with these fields”
  • “must not call external gateway if validation fails”

4) The over-mocking failure mode

Symptoms:

  • tests fail on refactor
  • tests mirror internal method calls
  • tests are longer than production code

Fix:

  • move logic into pure functions/domain layer
  • replace mocks with fakes
  • assert on observable behavior

5) Async + mocking pitfalls

Common issues:

  • forgetting ReturnsAsync
  • deadlocks caused by mixing sync-over-async
  • not controlling concurrency deterministically

Lead rule:

  • if concurrency is essential, consider modeling it with deterministic schedulers or higher-level integration tests.

6) Alternatives to mocks

  • In-memory fakes (repos, queues)
  • Contract tests for external dependencies
  • TestContainers for DB realism
  • WireMock-style HTTP stubs (when you own the stub)

7) Interview angle

Be ready to answer:

  • “When should you mock?”
  • “Why is EF InMemory not a real integration test?”
  • “How do you prevent brittle tests?”

A TL answer includes policy + tradeoffs + examples.


8) Review checklist

  • [ ] Team has a documented mocking policy.
  • [ ] Most unit tests are state-based.
  • [ ] Mocks are used at boundaries.
  • [ ] Fakes exist for common rich dependencies.
  • [ ] Over-mocking is treated as a maintainability smell.

📚 Related Articles