Answer
No, do not mock all dependencies in unit tests: replace only what leaves the process or changes between runs, such as HTTP APIs, email, the database and the clock, and test the unit with your own in-process classes running for real.
Verdict on the code under testYes
Why
- Blast radiususers
- Change frequencyregularly
- Detectabilityeventually
- Reversibilitywith-effort
- Test costmoderate
No, mock only the dependencies that leave the process or change between runs, and let the unit call your own classes for real. In the typical backend service with three classes of its own, a repository and a partner API, mocking everything leaves the calls between your classes untested. Blast radius is users and Change frequency is regularly, because those classes change about once a month. Detectability is eventually, because a mock keeps returning the old answer after the real class changes, and Reversibility is with-effort, because records saved with the wrong value need a repair script. Test cost is moderate, about an hour to set up the real collaborators, so rule R11 gives Test.
| When | Decision | Why |
|---|---|---|
| The unit charges a card through a payment gateway | Test mandatory: fake only the gateway, and assert one charge of the right amount per order | Blast radius rises to money and Reversibility to costly |
| A collaborator decides which records the current user may see | Test mandatory: run the real permission class with two users, and assert that each gets only their own records | Blast radius rises to safety-or-legal and Detectability to never |
| A partner API drops a field without notice, no sandbox shows it, and nobody reports the empty value | Test it differently: keep the fake server in unit tests, and alert on production responses that lack a field | Test cost rises to heavy, because only live responses show the change |
| The service only reads data for a page, and a broken call throws an error that users report the same day | Test minimally: one unit test of the main path with the real collaborators | Detectability moves to same-day and Reversibility to trivial |
| The service runs in an admin tool that only staff use | Test minimally: one unit test of the main path with the real collaborators | Blast radius falls to internal |
| Integration tests in CI run the service with its real collaborators, on data that gives each branch a different result | Do not add unit tests of the collaboration; rely on the integration tests | Detectability moves to immediately and Reversibility to trivial, because a broken call fails CI before merge |
What breaks if you don't test
A developer changes what a shared helper returns and updates the helper's tests. Every caller stays green, because each caller's mock still returns the old value. In production the callers pass on a wrong value, and a support ticket reports it days later.
What you lose if you over-test
A mock for every dependency pins the call graph: move a calculation from the mapper into the calculator, and every test that stubbed the mapper fails although the result is the same. With six mocked dependencies the stubs outgrow the assertion, and developers edit them to match the code without reading them. Going real everywhere costs too: a unit test that calls the partner's API fails when its sandbox is down.
How to test
- Use your own in-process classes as they are: value objects, calculators, validators, mappers. Google's test doubles chapter says "Prefer Realism Over Isolation".
- Replace what leaves the process or changes between runs: a partner API with a fake client, email with a fake mailer, the clock with a fixed clock.
- In unit tests of business rules, use an in-memory fake repository, and test the SQL against the production database engine in integration tests.
- Wrap each third-party client in your own interface and fake that interface; the Mockito wiki says "Don't mock a type you don't own!"
- Assert on results and stored state; verify a call only when the call is the behaviour, such as a charge.
When the answer changes
- A collaborator charges money or decides who sees which records.
- A partner changes its responses without notice, and no sandbox shows the change.
- Integration tests in CI run the unit with its real collaborators, on data that tells each branch apart.
Real incident + Code example
The mock that kept the old calendar
On an online shop I worked on, the product page showed a delivery date from a DeliveryEstimator, whose unit tests mocked every dependency, including our own WorkingCalendar. A colleague changed addWorkingDays() to count the dispatch day as day one for a warehouse report and updated the calendar's tests. The estimator tests stayed green, because the stub still returned the old dates. For three weeks the page promised delivery one working day early, and support answered about 140 "where is my parcel" tickets. The rewritten tests use the real calendar and a fake carrier:
@Test
void estimateSkipsHolidayAndWeekend() {
var calendar = new WorkingCalendar(Set.of(LocalDate.of(2026, 12, 25))); // real class
var carrier = new FakeCarrierClient(2); // replaces the HTTP API: 2 working days in transit
var estimator = new DeliveryEstimator(calendar, carrier);
// Dispatched on Wednesday 23 December; Friday 25 December is a holiday.
// A calendar that counts the dispatch day as day one returns 24 December.
assertEquals(LocalDate.of(2026, 12, 28), estimator.estimate(LocalDate.of(2026, 12, 23)));
}
Related questions
- Should I use mocks in unit tests?Code under test: Yes
- Should I test implementation details?No
- Should I test the service layer?Yes
- Should integration tests use mocks?Code under test: Yes
- Should tests use a real database?Yes
FAQ
- Should you mock everything in a unit test?
No, mock only the dependencies that leave the process or change between runs, such as HTTP APIs, email and the clock. A test with a mock for each class never checks the calls between classes.
- Should unit tests mock all dependencies?
No, a unit test should run the unit with its real in-process collaborators and replace only slow or nondeterministic ones. A test that runs three of your classes together is still a unit test when it needs no network or disk.
- Should I mock the database in unit tests?
No, replace the repository with an in-memory fake in unit tests of business rules, and test the SQL against the production database engine in integration tests. A mocked database returns whatever the test hands it, so the query never runs.
- What should I not mock in unit tests?
Do not mock value objects, your own classes that only compute, or types from a third-party library. Fake your own wrapper around a library client instead.