Mimics copy real behavior. Mocks copy expected behavior.
These two test doubles look similar but solve different problems. Knowing which to pick saves hours of debugging flaky tests.
What a Mimic Really Is
A mimic is a lightweight clone of a real object that exhibits the same outward reactions yet runs in memory. It answers the question “how does the real thing act?” without the cost of the real thing.
Think of a credit-card mimic that always approves a zip code, never contacts the bank, and still returns the same JSON shape the production gateway would. Teams use mimics to keep suites fast while preserving the original contract.
If the production service changes its response, the mimic must be updated manually; otherwise tests pass against an obsolete contract.
Typical Mimic Shape
Mimics are usually hand-coded classes that implement the same interface as the production dependency. They hold hard-coded happy-path answers and throw on unexpected input to signal contract drift.
A repository mimic might store a small List
What a Mock Really Is
A mock is an empty shell that records how it is called and can verify those calls later. It cares less about realistic answers and more about “was the caller polite?”
Mocks are generated by libraries such as Moq or Mockito at runtime. You never write their bodies; you only script their reactions and assertions.
A controller test can verify that the checkout service calls payment.charge exactly once with amount 999 and currency “USD”, regardless of what charge returns.
Typical Mock Shape
Mocks start life as a blank proxy. You attach canned returns and strict expectations, then assert invocation counts and argument values after the exercise phase.
Because the mock has no real logic, changing the production code’s call pattern breaks the test immediately, which is the desired safety net.
Core Intent Gap
Mimics protect the caller from slow or brittle dependencies. Mocks protect the caller from misusing dependencies.
Use a mimic when you need a cheaper twin that still talks the same language. Use a mock when you need a strict teacher that slaps your wrist on protocol violations.
Speed vs Safety Trade-off
Mimics run at memory speed, so suites stay below the second mark. The downside is silent drift when the real service evolves.
Mocks guarantee behavioral lock-in but cost extra boilerplate and can overspecify tests, making refactorings tedious.
Teams often start with mocks for early design feedback, then introduce mimics for integration-level coverage once the contract stabilizes.
When to Choose a Mimic
Pick a mimic when the real dependency is slow, flaky, or requires credentials you do not own in the CI container.
Payment gateways, cloud queues, and third-party SaaS endpoints are classic mimic candidates because their sandbox still costs network time.
Also favor mimics for read-only queries that return large payloads; a mimic can serve canned JSON in microseconds.
Red Flags for Mimics
Avoid mimics when the real component changes weekly; the maintenance tax outweighs the speed gain. If the contract is unstable, a mock or a real container is cheaper.
When to Choose a Mock
Choose a mock when the focus is interaction: order of calls, exact arguments, or absence of side effects.
Unit-testing a command handler that must publish exactly one DomainEvent and save one Aggregate is a textbook mock scenario.
Mocks also shine for sad paths: verify that logger.error is called once when the repository throws, without needing a real database to actually fail.
Red Flags for Mocks
Mocks overspecify when you assert every minor call; this leads to brittle tests that break on harmless refactors. Drop strict expectations that are irrelevant to the scenario under test.
Hybrid Approach
Some tests need both: a mimic for the query side and a mock for the command side. A checkout flow might mimic the product catalog for speed yet mock the payment gateway to verify the charge attempt.
This hybrid style keeps the test fast while still guarding the critical interaction. Name the variables clearly—catalogMimic vs paymentMock—to avoid reader confusion.
Naming Clues in Code
Suffixes guide future readers. “FakePaypal” signals a mimic, “paypalMock” signals a generated mock.
Consistency matters more than the exact suffix, so document the convention once in the README and stick to it across the codebase.
Common Pitfall: Overspecification
Developers often assert call counts that the requirement never mentions. This creates false negatives when internals change.
Ask “would a product owner care if this count changes?” If not, drop the assertion.
Common Pitfall: Silent Drift
A mimic that returns status 200 for four years can hide the fact that production now returns 202. Schedule contract tests against the real service to detect drift.
These tests can run nightly, not per commit, so they do not slow the inner loop.
Test Pyramid View
Mimics fit the service-layer tier where you want broad coverage with medium speed. Mocks live at the unit tier where you want pinpoint feedback in milliseconds.
End-to-end tests should use neither; they deserve real components to validate wiring.
Library Ecosystem
Java shops reach for Mockito for mocks and create hand-rolled fakes for mimics. .NET developers use Moq on one hand and write in-memory DbContext derivatives on the other.
Python teams often lean on unittest.mock for interactions and factory-boy for mimic-like model builders.
The tooling does not change the rule: know your intent first, then pick the library.
Refactoring Strategy
When a mock-heavy test becomes painful, extract a fake and promote the test one layer up the pyramid. The reverse is also valid: if a mimic grows too large, split the scenario and use focused mocks for edge cases.
Keep both versions in the codebase for a single commit to ensure green tests during the transition.
Communication Value
Well-named mimics act as living documentation of external contracts. New hires read the fake email sender and instantly learn what fields the real API expects.
Mocks document rules: “we log once, we retry twice, we never call billing for zero amount.” This doubles as design review.
Minimal Checklist
Ask “do I care about the answer or the question?” Answer → mimic, question → mock.
Check speed needs: over 500 ms per test case hints at a mimic or container, not a mock.
Finally, verify expectations against requirements, not implementation, to keep both doubles honest.