Skip to content

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

DoubleWhat it doesExample
Dummypassed but never used (fills a parameter)a null/placeholder arg
Stubreturns canned answers to callsa rate provider that always returns 0.9
Spya real object that also records callswrap a real list, verify add was called
Fakea working but simplified implementationan in-memory Map-backed repository
Mocka double with expectations you verifyassert 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 passed

Trap — 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) and ArgumentCaptor to 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

Lesson Quiz · Test Doubles & Mockito0 / 5
  1. Which double's purpose is to verify that an interaction happened?

    • AStub
    • BDummy
    • CMock
    • DFake
  2. You call an unstubbed method on a Mockito @Mock that returns an int. What do you get?

    • AAn exception
    • B0 (the default)
    • CA random value
    • Dnull
  3. verify(payment).charge(5, anyString()) throws InvalidUseOfMatchersException. Why?

    • A5 is too small
    • BIf any argument uses a matcher, all must — use eq(5)
    • Ccharge doesn't exist
    • DanyString() is deprecated
  4. What is an ArgumentCaptor for?

    • AStubbing return values
    • BCapturing the actual argument passed to a mock so you can assert on it
    • CCounting test runs
    • DMocking static methods
  5. Which is a good reason NOT to mock something?

    • AIt's a value object / record — just build a real one
    • BIt's an interface
    • CIt has more than one method
    • DIt's in another package

Next: Readable Assertions & Deterministic Tests.