Verification & Argument Capturing
Verifying Calls
verify(repo).save(any(User.class)); // called at least once
verify(repo, times(2)).findById(1L); // exactly 2 calls
verify(repo, never()).deleteById(anyLong()); // never called
verify(repo, atLeastOnce()).count();
verifyNoInteractions(emailService); // nothing touched it at all
verifyNoMoreInteractions(repo); // no calls beyond the ones already verifiedArgumentCaptor
@Captor
ArgumentCaptor<User> userCaptor;
@Test
void savesUserWithNormalizedEmail() {
userService.register("Ada", "ADA@Example.com");
verify(repo).save(userCaptor.capture());
assertEquals("ada@example.com", userCaptor.getValue().getEmail());
}doAnswer for Dynamic Behavior
when(repo.save(any(User.class))).thenAnswer(invocation -> {
User u = invocation.getArgument(0);
u.setId(42L); // simulate DB assigning an id
return u;
});BDD Style (given/willReturn)
import static org.mockito.BDDMockito.*;
given(repo.findById(1L)).willReturn(Optional.of(user));
userService.getUser(1L);
then(repo).should().findById(1L);
// purely stylistic alias for when/verify — same underlying mechanicsVerification Anti-Patterns
Verifying every single interaction and exact argument couples tests to implementation details rather than behavior — a harmless internal refactor can break many such tests without any real regression. Reserve verify() for interactions that represent genuinely important side effects (an email sent, a record deleted); prefer asserting on return values/state otherwise.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free