Lesson 12 / 25
Argument Matchers and Advanced Stubbing
Use argument matchers, answers and doReturn-style stubbing correctly.
Flexible stubs
Stubs match calls by argument equality by default. Argument matchers relax this: any(), any(Order.class), anyString(), anyLong(), eq(value), argThat(predicate) and more from ArgumentMatchers. A rule that catches everyone once: if any argument uses a matcher, all arguments must use matchers, so write when(repo.find(eq("pen"), anyInt())), not find("pen", anyInt()). thenAnswer(invocation -> ...) computes the result from the actual arguments, useful for echoing input back or generating IDs. For void methods, and for spies where calling the real method during stubbing would be harmful, use the doReturn(...).when(mock).method(...), doThrow(...) and doNothing() family. Avoid over-general matchers such as any() everywhere: tests should still fail if the code passes the wrong argument. Prefer stubbing with specific values and verifying important arguments with captors.
Matchers, answers and void stubbing
All-or-nothing matchers, computed answers and doThrow for a void method.
@ExtendWith(MockitoExtension.class)
class InventoryServiceTest {
@Mock StockRepository stock;
@Mock EventPublisher events;
@Test
void reservesWhenEnoughStock() {
when(stock.available(eq("pen"), anyString())).thenReturn(10); // matchers for all args
when(stock.reserve(anyString(), anyInt()))
.thenAnswer(inv -> "R-" + inv.getArgument(0) + "-" + inv.getArgument(1));
var service = new InventoryService(stock, events);
String reservationId = service.reserve("pen", 3, "WH-PUNE");
assertThat(reservationId).isEqualTo("R-pen-3");
}
@Test
void publishFailureIsWrapped() {
when(stock.available("pen", "WH-PUNE")).thenReturn(10);
when(stock.reserve("pen", 3)).thenReturn("R-1");
doThrow(new BrokerUnavailableException()).when(events).publish(any(StockReserved.class)); // void method
var service = new InventoryService(stock, events);
assertThatThrownBy(() -> service.reserve("pen", 3, "WH-PUNE"))
.isInstanceOf(ReservationNotConfirmedException.class);
}
}Matchers are all or nothing
Mixing a raw value with a matcher, as in when(repo.find("pen", anyInt())), throws InvalidUseOfMatchersException. Wrap the raw value in eq("pen").
Quick check: How do you stub a void method to throw an exception in Mockito?
- doThrow(ex).when(mock).voidMethod()
- when(mock.voidMethod()).thenThrow(ex)
- mock.voidMethod().thenThrow(ex)
- Void methods cannot be stubbed
Answer
doThrow(ex).when(mock).voidMethod() — when(...) needs a return value; void methods use the doThrow/doReturn family.