# Argument Matchers and Advanced Stubbing — JUnit 5 & Mockito

Source: https://www.skillbyai.com/en/java-testing/m-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.

```java
@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")`.

**Quiz:** How do you stub a void method to throw an exception in Mockito?

- [x] 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.
