Now that you know how to create mocks and stub methods, let us dig into verification and more advanced stubbing patterns. These are the tools that make your tests truly powerful and precise.
Verifying Interactions
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
class VerificationTest {
@Test
void verifyMethodCalled() {
List<String> mockList = mock(List.class);
mockList.add("hello");
mockList.add("world");
// Verify add was called twice
verify(mockList, times(2)).add(anyString());
// Verify add was called once with specific arg
verify(mockList).add("hello");
// Verify add was never called with "foo"
verify(mockList, never()).add("foo");
// Verify no other interactions
verifyNoMoreInteractions(mockList);
}
}
Verification Times
verify(mock).method(); // exactly once (default)
verify(mock, never()).method(); // never
verify(mock, times(3)).method(); // exactly 3 times
verify(mock, atLeast(1)).method(); // at least once
verify(mock, atMost(5)).method(); // at most 5 times
verify(mock, atLeastOnce()).method(); // at least once
verify(mock, exactly(2)).method(); // exactly 2 times
Argument Matchers
import static org.mockito.ArgumentMatchers.*;
class ArgumentMatcherDemo {
@Test
void argumentMatching() {
when(mock.get(anyString())).thenReturn("value");
when(mock.get(eq("exact"))).thenReturn("exact value");
assertEquals("exact value", mock.get("exact"));
assertEquals("value", mock.get("anything"));
// Useful matchers:
// anyString() - any string
// anyInt() - any int
// any(Class) - any of that type
// eq(value) - exact value
// contains("sub") - string contains
// startsWith("pre") - string starts with
// endsWith("suf") - string ends with
// matches("regex") - regex match
// argThat(arg -> arg > 5) - custom matcher
}
}
Advanced Stubbing
class AdvancedStubbingTest {
@Test
void multipleReturnValues() {
when(mock.iterator())
.thenReturn(Iterator.of("a", "b"))
.thenThrow(new RuntimeException())
.thenReturn(Iterator.of("c"));
assertEquals("a", mock.iterator().next());
assertEquals("b", mock.iterator().next());
assertThrows(RuntimeException.class, () -> mock.iterator());
assertEquals("c", mock.iterator().next());
}
@Test
void doReturnWhen() {
// Use doReturn when you want to avoid calling the real method
doReturn("mocked").when(mock).toString();
// Use doThrow for void methods
doThrow(new RuntimeException("error"))
.when(mock).someVoidMethod();
// Use doNothing for void methods
doNothing().when(mock).anotherVoidMethod();
}
@Test
void thenAnswer() {
when(mock.get(anyString()))
.thenAnswer(invocation -> {
String arg = invocation.getArgument(0);
return "processed: " + arg;
});
assertEquals("processed: hello", mock.get("hello"));
}
}
Verification Timeout
@Test
void asyncVerification() {
// Start async operation
service.startAsync();
// Wait up to 2 seconds for verification
verify(mock, timeout(2000).atLeastOnce()).callback();
// Or with more options
verify(mock, timeout(5000).times(3)).asyncMethod();
}
InOrder Verification
@Test
void verifyCallOrder() {
mock.first();
mock.second();
mock.third();
InOrder inOrder = inOrder(mock);
inOrder.verify(mock).first();
inOrder.verify(mock).second();
inOrder.verify(mock).third();
}
Resetting Mocks
@Test
void resetDemo() {
when(mock.get("key")).thenReturn("value");
assertEquals("value", mock.get("key"));
reset(mock); // Clears all stubbings and interactions
// Now mock returns default value
assertNull(mock.get("key"));
}
Complete Example
@ExtendWith(MockitoExtension.class)
class NotificationServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private MessageQueue messageQueue;
@InjectMocks
private NotificationService service;
@Test
@DisplayName("Should send notification to active users")
void shouldNotifyActiveUsers() {
User user1 = new User("John", true);
User user2 = new User("Jane", false);
when(userRepository.findActiveUsers())
.thenReturn(List.of(user1, user2));
service.sendBulkNotification("Hello!");
verify(userRepository).findActiveUsers();
verify(messageQueue).enqueue(argThat(msg ->
msg.getRecipient().equals("John")
));
verify(messageQueue, never()).enqueue(argThat(msg ->
msg.getRecipient().equals("Jane")
));
}
}