BackendBench
Coffee

Unravel › Java concepts › Testing

Testing

Mockito: matchers, verification, spies

Beyond when(...).thenReturn(...): argument matchers (any(), eq()), verification modes (times(2), never()), inOrder, captured arguments, spies (real objects with some methods replaced) and doReturn().when(spy).

Learn these first

At a glance

Mental model

Hiring an actor to play a colleague: you give them lines for the questions you expect, and afterwards check the recorded log of what was asked and in which order.

What happens, in order
  1. 1Matchers: when(repo.find(anyLong())).thenReturn(order)If one argument uses a matcher, all must: eq("a") for the literal ones.
  2. 2Verification: verify(mailer, times(1)).send(any()), never(), atLeastOnce(), verifyNoMoreInteractions(mailer), and InOrder inOrder = inOrder(a, b); inOrder.verify(a).x(); inOrder.verify(b).y();
  3. 3Stubbing side effects and errors: doThrow(new IOException()).when(out).write(any()) works also for void methods; thenAnswer(inv -> inv.getArgument(0)) returns the input
  4. 4ArgumentCaptor<Order> captor = ArgumentCaptor.forClass(Order.class); verify(repo).save(captor.capture()); then assert on captor.getValue()
  5. 5spy(realObject) calls real methods unless stubbed; use doReturn(x).when(spy).m() so the real method is not called while stubbingmockStatic (with try-with-resources) fakes static methods.
Remember
Watch out
Mixing matchers and raw values in one callVerifying every call instead of the outcomeUsing when(spy.m()) which executes the real methodMocking types you do not own and then drifting from reality
Example (highlighted lines explain it)
import static org.mockito.Mockito.*;import static org.mockito.ArgumentMatchers.*;import org.mockito.ArgumentCaptor;class Notifier {  interface Mailer { void send(String to, String body); }  interface Users { String emailOf(long id); }  static void notifyUser(Users users, Mailer mailer, long id) { mailer.send(users.emailOf(id), "hi"); }  static void check() {    Users users = mock(Users.class);    Mailer mailer = mock(Mailer.class);    when(users.emailOf(anyLong())).thenReturn("a@x.org");    notifyUser(users, mailer, 7L);    ArgumentCaptor<String> to = ArgumentCaptor.forClass(String.class);    verify(mailer, times(1)).send(to.capture(), eq("hi"));    verifyNoMoreInteractions(mailer);  }}
  1. line 2 uses ArgumentMatchers, the core of Mockito: matchers, verification, spies
  2. line 11 uses any, the core of Mockito: matchers, verification, spies
  3. line 14 uses times, the core of Mockito: matchers, verification, spies
  4. line 15 uses verifyNoMoreInteractions, the core of Mockito: matchers, verification, spies
Read the full explanation (about 1 min)

Beyond when(...).thenReturn(...): argument matchers (any(), eq()), verification modes (times(2), never()), inOrder, captured arguments, spies (real objects with some methods replaced) and doReturn().when(spy).

A mental model

Hiring an actor to play a colleague: you give them lines for the questions you expect, and afterwards check the recorded log of what was asked and in which order.

How it works

Matchers: when(repo.find(anyLong())).thenReturn(order). If one argument uses a matcher, all must: eq("a") for the literal ones.

Verification: verify(mailer, times(1)).send(any()), never(), atLeastOnce(), verifyNoMoreInteractions(mailer), and InOrder inOrder = inOrder(a, b); inOrder.verify(a).x(); inOrder.verify(b).y();.

Stubbing side effects and errors: doThrow(new IOException()).when(out).write(any()) works also for void methods; thenAnswer(inv -> inv.getArgument(0)) returns the input.

ArgumentCaptor<Order> captor = ArgumentCaptor.forClass(Order.class); verify(repo).save(captor.capture()); then assert on captor.getValue().

spy(realObject) calls real methods unless stubbed; use doReturn(x).when(spy).m() so the real method is not called while stubbing. mockStatic (with try-with-resources) fakes static methods.

Going deeper

Mock the collaborators at your boundaries (repositories, HTTP clients), not value objects, and not the class under test. Too many mocks and verify calls couple the test to the implementation: refactoring breaks tests without a bug.

Strict stubs (default with the Mockito extension) fail on unused stubbings, which cleans up tests. lenient() turns that off for one stub.

Prefer fakes (an in-memory repository) when the interaction is complex, and test the real thing with an integration test.

Under the hood

Mockito creates a subclass (or uses an inline mock maker with instrumentation for finals and statics) whose methods record the invocation and look up a stubbed answer in the mock's handler. Matchers are registered in a thread-local stack just before the call, which is why you cannot mix them with raw values.

Common mistakes

  • Mixing matchers and raw values in one call.
  • Verifying every call instead of the outcome.
  • Using when(spy.m()) which executes the real method.
  • Mocking types you do not own and then drifting from reality.

How Unravel recognises it

When you paste code, Unravel looks for these signs of mockito: matchers, verification, spies. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Mockito: matchers, verification, spies?

Beyond when(...).thenReturn(...): argument matchers (any(), eq()), verification modes (times(2), never()), inOrder, captured arguments, spies (real objects with some methods replaced) and doReturn().when(spy).

What are the most common mistakes with Mockito: matchers, verification, spies?

Mixing matchers and raw values in one call. Verifying every call instead of the outcome. Using when(spy.m()) which executes the real method. Mocking types you do not own and then drifting from reality.

How can I see Mockito: matchers, verification, spies in real code?

Paste your own Java into Unravel and it will point out where mockito: matchers, verification, spies appears, explain how it works at the detail level you choose, and draw the flow of the code around it.