Unravel › Java concepts › Testing
Advanced Mockito (matchers, captors, static mocks)
Mockito does more than when(...).thenReturn(...).
Learn these first
At a glance
A stunt double with a notebook.
- 1Verification: verify(mailer, times(2)).send(any()), never(), atLeastOnce(), inOrder(a, b), and verifyNoMoreInteractions(mailer) to prove nothing else happened
- 2Capturing: ArgumentCaptor<Email> captor = ArgumentCaptor.forClass(Email.class); verify(mailer).send(captor.capture()); assertEquals("Hi", captor.getValue().subject()); checks the object that was actually sent
- 3Stubbing void methods and exceptions: doThrow(new IOException()).when(client).upload(any()), doAnswer(inv -> ...)BDD style: given(repo.find(1L)).willReturn(order).
- 4Matchers: any(), eq(), argThat(x -> x.total() > 0); when one argument uses a matcher, all of them must (eq("a") for the raw ones)
- mockStatic replaces a static method for the current thread inside a try-with-resources block
- Strict stubs (default with MockitoExtension) fail tests that set up stubbing never used, which finds dead set-up
- Mockito generates a subclass or inline instrumentation (ByteBuddy) of the mocked type
@ExtendWith(MockitoExtension.class)class NotifierTest { @Mock Mailer mailer; @InjectMocks Notifier notifier; @Test void sendsOneEmailWithOrderNumber() { notifier.orderShipped(new Order("o-7", "ana@acme.com")); ArgumentCaptor<Email> captor = ArgumentCaptor.forClass(Email.class); verify(mailer, times(1)).send(captor.capture()); assertTrue(captor.getValue().body().contains("o-7")); verifyNoMoreInteractions(mailer); }}- line 1 uses MockitoExtension, the core of Advanced Mockito (matchers, captors, static mocks)
- line 10 uses forClass, the core of Advanced Mockito (matchers, captors, static mocks)
- line 13 uses verifyNoMoreInteractions, the core of Advanced Mockito (matchers, captors, static mocks)
Read the full explanation (about 1 min)
Mockito does more than when(...).thenReturn(...). You can verify call order and counts, capture the arguments that were passed, stub void methods and exceptions, and in rare cases replace static methods or constructors.
A mental model
A stunt double with a notebook. Besides standing in for the real actor, the double writes down exactly what was said to them, in which order, and how many times.
How it works
Verification: verify(mailer, times(2)).send(any()), never(), atLeastOnce(), inOrder(a, b), and verifyNoMoreInteractions(mailer) to prove nothing else happened.
Capturing: ArgumentCaptor<Email> captor = ArgumentCaptor.forClass(Email.class); verify(mailer).send(captor.capture()); assertEquals("Hi", captor.getValue().subject()); checks the object that was actually sent.
Stubbing void methods and exceptions: doThrow(new IOException()).when(client).upload(any()), doAnswer(inv -> ...). BDD style: given(repo.find(1L)).willReturn(order).
Matchers: any(), eq(), argThat(x -> x.total() > 0); when one argument uses a matcher, all of them must (eq("a") for the raw ones).
Going deeper
mockStatic replaces a static method for the current thread inside a try-with-resources block. It is a last resort for legacy code (LocalDate.now(), utility classes); inject a Clock or a wrapper instead to avoid it.
Strict stubs (default with MockitoExtension) fail tests that set up stubbing never used, which finds dead set-up. Use lenient() only on purpose.
Over-verification couples tests to the implementation: if every internal call is verified, refactoring breaks tests that did not need to. Verify outcomes and important interactions (a message sent, a payment charged), not every method call.
Under the hood
Mockito generates a subclass or inline instrumentation (ByteBuddy) of the mocked type. Every call on the mock is recorded in an invocation container; when(...) registers a stubbing against the last invocation; verify matches recorded invocations against your expectation and the matchers on the argument stack.
Common mistakes
- Mixing matchers and raw values in one call.
- Verifying every internal call.
- Static mocking as the default tool.
- Stubbing the class under test (a spy) instead of its collaborators.
How Unravel recognises it
When you paste code, Unravel looks for these signs of advanced mockito (matchers, captors, static mocks). Spotting them yourself is a good habit when you read unfamiliar code:
- Types:
BDDMockitoArgumentMatchersInOrderMockedStaticMockedConstructionMockitoAnnotationsMockitoExtension - Method calls:
mockStatic()mockConstruction()doReturn()doThrow()doAnswer()doNothing()willReturn()willThrow()inOrder()verifyNoMoreInteractions()verifyNoInteractions()argThat()lenient()never()and 3 more
Part of a learning path
- Testing enterprise code · previous: Mockito mocks · next: Spring Boot test slices and test configuration
Related concepts
Frequently asked questions
What is Advanced Mockito (matchers, captors, static mocks)?
Mockito does more than when(...).thenReturn(...). You can verify call order and counts, capture the arguments that were passed, stub void methods and exceptions, and in rare cases replace static methods or constructors.
What are the most common mistakes with Advanced Mockito (matchers, captors, static mocks)?
Mixing matchers and raw values in one call. Verifying every internal call. Static mocking as the default tool. Stubbing the class under test (a spy) instead of its collaborators.
How can I see Advanced Mockito (matchers, captors, static mocks) in real code?
Paste your own Java into Unravel and it will point out where advanced mockito (matchers, captors, static mocks) appears, explain how it works at the detail level you choose, and draw the flow of the code around it.