# Verifying Interactions and Capturing Arguments — JUnit 5 & Mockito

Source: https://www.skillbyai.com/en/java-testing/v-verify

> Verify calls with times, never and inOrder, and capture arguments for assertions.

## Checking that the right things happened

When the important outcome of a method is a **side effect** on a collaborator (an email sent, an event published, a payment requested), verify it. **`verify(mock).method(args)`** checks that the call happened exactly once; **`verify(mock, times(2))`**, **`never()`**, **`atLeastOnce()`** and **`atMost(n)`** adjust the count. **`verifyNoMoreInteractions(mock)`** asserts that nothing else was called, which is strict and can make tests brittle, so use it sparingly. **`InOrder`** verifies the order of calls across mocks when sequence matters (charge payment before marking the order paid). **`ArgumentCaptor`** captures the actual argument passed so you can make detailed assertions on it, which is clearer than complex `argThat` matchers; declare it with `@Captor` or `ArgumentCaptor.forClass(...)`. Verify **outcomes that matter to the behaviour**, not every internal call: a test that verifies that a repository's `findById` was called is usually testing implementation details.

## Capturing what was sent

The test captures the object passed to a collaborator and asserts on its contents.

![A box sending an envelope arrow to a second box, with a small net catching the envelope midway for inspection.](assets/figures/java-testing/section-5-map.svg) — Figure 5.1 — An argument captor intercepting a call for assertions.

## Verification, captors and ordering

Assert on the email actually sent and on the order of operations.

```java
@ExtendWith(MockitoExtension.class)
class CheckoutServiceTest {

    @Mock PaymentGateway payments;
    @Mock OrderRepository orders;
    @Mock Mailer mailer;
    @Captor ArgumentCaptor<Email> emailCaptor;

    @Test
    void successfulCheckoutChargesThenConfirmsAndEmails() {
        when(payments.charge(any(), anyLong())).thenReturn(PaymentResult.approved("pay_1"));
        when(orders.save(any())).thenAnswer(inv -> inv.getArgument(0));
        var service = new CheckoutService(payments, orders, mailer);

        service.checkout(cart("asha@example.com", 49_900));

        InOrder inOrder = inOrder(payments, orders);
        inOrder.verify(payments).charge(any(), eq(49_900L));
        inOrder.verify(orders).save(argThat(o -> o.status() == OrderStatus.PAID));

        verify(mailer).send(emailCaptor.capture());
        Email sent = emailCaptor.getValue();
        assertThat(sent.to()).isEqualTo("asha@example.com");
        assertThat(sent.subject()).contains("Order confirmed");
    }

    @Test
    void declinedPaymentSendsNoEmail() {
        when(payments.charge(any(), anyLong())).thenReturn(PaymentResult.declined("insufficient funds"));
        var service = new CheckoutService(payments, orders, mailer);

        assertThatThrownBy(() -> service.checkout(cart("asha@example.com", 49_900)))
            .isInstanceOf(PaymentDeclinedException.class);
        verify(mailer, never()).send(any());
    }
}
```

## Stub queries, verify commands

A useful rule: stub methods that return data (queries) and verify methods that change things (commands). Verifying a query that was only stubbed adds brittleness without testing behaviour.

**Quiz:** Which Mockito feature lets you inspect the actual object passed to a mocked method?

- [x] ArgumentCaptor
- [ ] InOrder
- [ ] thenAnswer
- [ ] doNothing

*Answer:* ArgumentCaptor. ArgumentCaptor captures arguments so the test can assert on their contents.
