On this page
Mockito Advanced
Advanced Mockito features for partial mocking, capturing arguments, and mocking static methods.
Spy — Partial Mock
A spy wraps a real object, allowing you to stub specific methods while calling real ones:
List<String> list = new ArrayList<>(List.of("a", "b"));
List<String> spy = spy(list);
spy.add("c"); // real method — list now has 3 items
when(spy.size()).thenReturn(100); // stub size()
assertEquals(100, spy.size()); // stubbed
assertEquals(3, list.size()); // real list modified
Important: Use doReturn().when(spy) for spies, not when().thenReturn() — avoids calling the real method during stubbing.
doReturn(100).when(spy).size();
ArgumentCaptor
Capture arguments passed to mocked methods for detailed assertions:
@Test
void shouldSaveUserWithCorrectData() {
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
userService.createUser("Alice", "[email protected]");
verify(repository).save(captor.capture());
User saved = captor.getValue();
assertEquals("Alice", saved.getName());
assertEquals("[email protected]", saved.getEmail());
}
Multiple captures:
verify(repository, times(2)).save(captor.capture());
List<User> allSaved = captor.getAllValues();
assertEquals(2, allSaved.size());
Answer — Custom Stub Logic
when(repository.findById(anyLong())).thenAnswer(invocation -> {
Long id = invocation.getArgument(0);
return Optional.of(new User(id, "User-" + id));
});
when(repository.save(any())).thenAnswer(invocation -> {
User user = invocation.getArgument(0);
user.setId(UUID.randomUUID().getMostSignificantBits());
return user;
});
Static Method Mocking (Mockito 3.4+)
Requires mockito-inline:
try (MockedStatic<IdGenerator> mocked = mockStatic(IdGenerator.class)) {
mocked.when(IdGenerator::nextId).thenReturn(42L);
User user = userService.createUser("Alice");
assertEquals(42L, user.getId());
}
Mock Construction (Mockito 3.5+)
Mock object construction:
try (MockedConstruction<ExternalClient> mocked =
mockConstruction(ExternalClient.class, (mock, context) -> {
when(mock.fetchData()).thenReturn("mocked data");
})) {
Service service = new Service();
assertEquals("mocked data", service.process());
assertEquals(1, mocked.constructed().size());
}
Timeout Verification
verify(repository, timeout(1000)).save(any());
verify(repository, after(500).times(2)).findAll();
Reset Mocks
reset(repository); // clears stubbing and interaction history
Use sparingly — usually indicates tests are not independent.
Best Practices
- Prefer
@Mockover@Spy— spies are for partial mocking only - Use
ArgumentCaptorwhen you need to inspect what was passed to a mock - Always close
MockedStaticandMockedConstruction(use try-with-resources) - Static mocking is a last resort — consider refactoring for testability
- Avoid mocking types you do not own — wrap them in an adapter instead