Mocking and Stubbing with Mockito
This chapter covers the full Mockito stubbing and verification API — how to control what mocks return and verify what was called.
Stubbing — Controlling Mock Behavior
when/thenReturn
when(userRepository.findById(1L)).thenReturn(Optional.of(alice));
when(userRepository.findById(999L)).thenReturn(Optional.empty());
when(userRepository.count()).thenReturn(42L);
when/thenThrow
when(paymentGateway.charge(any(), any()))
.thenThrow(new PaymentException("Gateway timeout"));
Consecutive Returns
when(tokenService.generateToken())
.thenReturn("token-1")
.thenReturn("token-2")
.thenReturn("token-3");
// First call → "token-1", second → "token-2", third+ → "token-3"
thenAnswer — Dynamic Responses
when(userRepository.save(any(User.class))).thenAnswer(invocation -> {
User user = invocation.getArgument(0);
user.setId(100L); // simulate auto-generated ID
return user;
});
doReturn/when vs when/thenReturn
// Standard — works for most cases
when(mock.method()).thenReturn(value);
// Use doReturn/when for:
// 1. Spies (to avoid calling real method during stubbing)
doReturn(value).when(spy).method();
// 2. Void methods
doNothing().when(mock).voidMethod();
doThrow(new RuntimeException()).when(mock).voidMethod();
Stubbing Void Methods
// Do nothing (default behavior, but explicit)
doNothing().when(emailService).sendEmail(any());
// Throw exception
doThrow(new MailException("SMTP down"))
.when(emailService).sendEmail("bad@email");
// Execute custom logic
doAnswer(invocation -> {
String email = invocation.getArgument(0);
System.out.println("Would send email to: " + email);
return null; // void method
}).when(emailService).sendEmail(any());
Argument Matchers
When stubbing, you can match arguments flexibly:
// Exact value
when(repo.findById(1L)).thenReturn(Optional.of(alice));
// Any value of type
when(repo.findById(anyLong())).thenReturn(Optional.of(alice));
when(repo.save(any(User.class))).thenReturn(alice);
// Any string
when(repo.findByEmail(anyString())).thenReturn(Optional.empty());
// Null
when(repo.findByEmail(isNull())).thenThrow(new IllegalArgumentException());
// Custom matcher
when(repo.findByEmail(argThat(email -> email.endsWith("@test.com"))))
.thenReturn(Optional.of(testUser));
Common Matchers
| Matcher | Matches |
|---|---|
any() |
Any value (including null) |
any(Class.class) |
Any instance of type |
anyString() |
Any non-null String |
anyInt(), anyLong() |
Any primitive value |
anyList(), anyMap() |
Any collection |
eq(value) |
Exact value (use when mixing matchers) |
isNull() |
Null value |
argThat(predicate) |
Custom condition |
Matcher Rule: All or Nothing
// ❌ WRONG: Can't mix raw values with matchers
when(service.process("literal", anyInt())).thenReturn(result);
// ✅ CORRECT: Use eq() for literal values
when(service.process(eq("literal"), anyInt())).thenReturn(result);
Verification — What Was Called?
Basic Verification
// Was this method called?
verify(emailService).sendWelcomeEmail("alice@test.com");
// Was it called with any argument?
verify(emailService).sendWelcomeEmail(anyString());
Call Count Verification
// Exactly once (default)
verify(repo, times(1)).save(any());
// Exactly N times
verify(repo, times(3)).save(any());
// Never called
verify(repo, never()).delete(any());
// At least / at most
verify(repo, atLeastOnce()).findById(anyLong());
verify(repo, atLeast(2)).save(any());
verify(repo, atMost(5)).findAll();
Verify No More Interactions
verify(emailService).sendWelcomeEmail(any());
verifyNoMoreInteractions(emailService);
// Fails if any other method was called on emailService
Verify Zero Interactions
verifyNoInteractions(auditLogger);
// Fails if ANY method was called on auditLogger
Verification Order
InOrder inOrder = inOrder(userRepository, emailService);
inOrder.verify(userRepository).save(any(User.class));
inOrder.verify(emailService).sendWelcomeEmail(anyString());
// Ensures save was called BEFORE sendWelcomeEmail
Complete Example
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock private OrderRepository orderRepo;
@Mock private PaymentGateway payment;
@Mock private InventoryService inventory;
@Mock private NotificationService notifications;
@InjectMocks private OrderService orderService;
@Test
void shouldProcessOrderEndToEnd() {
// Arrange
Order order = new Order("ORD-1", List.of(
new LineItem("SKU-A", 2),
new LineItem("SKU-B", 1)
));
when(inventory.checkStock("SKU-A")).thenReturn(true);
when(inventory.checkStock("SKU-B")).thenReturn(true);
when(payment.charge(any())).thenReturn(PaymentResult.success("TXN-123"));
when(orderRepo.save(any())).thenAnswer(inv -> {
Order o = inv.getArgument(0);
o.setStatus(OrderStatus.CONFIRMED);
return o;
});
// Act
Order result = orderService.processOrder(order);
// Assert
assertEquals(OrderStatus.CONFIRMED, result.getStatus());
// Verify interactions in order
InOrder inOrder = inOrder(inventory, payment, orderRepo, notifications);
inOrder.verify(inventory).checkStock("SKU-A");
inOrder.verify(inventory).checkStock("SKU-B");
inOrder.verify(payment).charge(any());
inOrder.verify(orderRepo).save(any());
inOrder.verify(notifications).sendOrderConfirmation("ORD-1");
}
@Test
void shouldNotChargeWhenOutOfStock() {
Order order = new Order("ORD-2", List.of(new LineItem("SKU-X", 1)));
when(inventory.checkStock("SKU-X")).thenReturn(false);
assertThrows(OutOfStockException.class,
() -> orderService.processOrder(order));
verify(payment, never()).charge(any());
verify(orderRepo, never()).save(any());
verify(notifications, never()).sendOrderConfirmation(any());
}
}
Common Mistakes
| Mistake | Fix |
|---|---|
| Mixing raw values and matchers | Use eq() for literals when other args use matchers |
| Verifying stubbed calls (redundant) | Only verify side effects and important interactions |
| Over-verifying | Don’t verify every call — focus on behavior |
Forgetting doReturn for spies |
when(spy.method()) calls the real method first |
Key Takeaways
when/thenReturn— control what mocks return.doReturn/when— use for void methods and spies.- Argument matchers —
any(),eq(),argThat()for flexible matching. verify— confirm interactions happened (or didn’t).- Don’t over-verify — test behavior, not implementation details.
What’s Next?
Next: ArgumentCaptor — capture and inspect the exact objects passed to mock methods.