Appearance
Lesson 03 · Test Doubles & Mockito
Beyond the 1Z0-830 exam
To test a unit in isolation you replace its collaborators with test doubles. Mockito is the standard Java library for that. The skill is knowing which double to use — and when not to mock at all.
Objectives
After this lesson you will be able to:
- Distinguish dummy, stub, spy, fake, and mock.
- Stub return values and verify interactions with Mockito.
- Match arguments and capture them with an
ArgumentCaptor. - Decide when mocking helps and when it hurts.
The five kinds of double
| Double | What it does | Example |
|---|---|---|
| Dummy | passed but never used (fills a parameter) | a null/placeholder arg |
| Stub | returns canned answers to calls | a rate provider that always returns 0.9 |
| Spy | a real object that also records calls | wrap a real list, verify add was called |
| Fake | a working but simplified implementation | an in-memory Map-backed repository |
| Mock | a double with expectations you verify | assert email.send(...) was called once |
The crucial distinction: a stub helps you control inputs (state the SUT reads); a mock lets you assert outputs as interactions (calls the SUT makes). Many doubles play both roles.
Mockito basics — stub and verify
java
@ExtendWith(MockitoExtension.class)
class PricingServiceTest {
@Mock RateProvider rates; // Mockito creates the double
@Test
void quotesUsingRate() {
when(rates.rateFor("EUR")).thenReturn(0.90); // STUB: control the input
var svc = new PricingService(rates, clock);
var q = svc.quote(1000, "EUR");
assertThat(q.convertedCents()).isEqualTo(900);
verify(rates).rateFor("EUR"); // MOCK: assert the interaction happened
}
}@Mock+@ExtendWith(MockitoExtension.class)creates and injects doubles.when(...).thenReturn(...)stubs a return value;thenThrow(...)stubs an exception.verify(mock).method(args)asserts it was called;verify(mock, never()).x()/times(2)/verify(mock, never()).x()count calls.
Gotcha
An unstubbed method on a mock returns a default: 0, false, null, or an empty collection — not an error. A test that "mysteriously" sees null or 0 usually forgot to stub a call. (A spy, by contrast, calls the real method unless you stub it.)
Argument matchers and captors
Match flexibly, or capture the actual argument to assert on it:
java
when(repo.findById(anyLong())).thenReturn(Optional.of(user)); // matcher
ArgumentCaptor<Order> sent = ArgumentCaptor.forClass(Order.class);
verify(gateway).submit(sent.capture());
assertThat(sent.getValue().total()).isEqualTo(1000); // inspect what was passedTrap — mixing matchers and raw values
If any argument uses a matcher, all must: verify(m).pay(eq(5), anyString()), not verify(m).pay(5, anyString()). Mixing a raw 5 with a matcher throws InvalidUseOfMatchersException.
When NOT to mock
Mocks are a sharp tool; overusing them produces brittle tests that assert implementation rather than behavior.
- Don't mock value objects (records,
String,List) — just create real ones. - Don't mock types you don't own without a thin wrapper — when the real API changes, your mock still "passes" against an outdated contract (a false green).
- Prefer a real or fake for simple collaborators (an in-memory repo beats a heavily-stubbed mock).
- Mocking everything couples the test to the call sequence; refactoring then breaks tests even when behavior is unchanged.
SDET note
Inject collaborators through the constructor (as PricingService does with RateProvider and Clock) — that's what makes a class mockable at all. Code that news its dependencies internally or calls statics can't be isolated without heavier tooling. Testability is a design property.
Key Takeaways
- Five doubles: dummy, stub, spy, fake, mock. Stubs control inputs; mocks verify interactions.
- Mockito:
@Mock+MockitoExtension,when(...).thenReturn(...)to stub,verify(...)to assert calls. - Unstubbed mock methods return defaults (
null/0/false/empty), not errors — a common source of confusion. - Use matchers (
any,eq) consistently (all-or-none) andArgumentCaptorto inspect passed arguments. - Don't over-mock: skip value objects, wrap types you don't own, prefer fakes/real for simple collaborators. Mockability comes from constructor injection.
Lesson Quiz
Which double's purpose is to verify that an interaction happened?
You call an unstubbed method on a Mockito @Mock that returns an int. What do you get?
verify(payment).charge(5, anyString()) throws InvalidUseOfMatchersException. Why?
What is an ArgumentCaptor for?
Which is a good reason NOT to mock something?