How do you match a complex argument by some of its fields using argThat(), and what are the trade-offs versus ArgumentCaptor?
answer
- argThat = lambda arg -> boolean (ArgumentMatcher)
- captor = capture then assert afterward
- argThat: weak failure messages unless custom toString
- captor: rich diagnostics, verification only
- both still obey the all-matchers rule; keep predicates pure
basics
~20 sargThat() lets you pass a custom predicate that decides whether an argument matches — e.g. accept any Order whose total is over 100. Use it for inline rules; use ArgumentCaptor when you want to grab the actual argument and run rich assertions on it.
solid answer
~50 s`argThat(predicate)` is the escape hatch for matching arguments by custom logic: you give it an `ArgumentMatcher<T>` (a lambda `arg -> boolean`) and Mockito uses it like any other matcher in `when`/`verify`. It shines when you only care about a subset of fields — `verify(repo).save(argThat(o -> o.getStatus() == PAID))`. Trade-offs versus `ArgumentCaptor`: argThat keeps the assertion *inline* with the verification and can re-match across multiple calls, but on failure it gives a poor diagnostic (just "wanted but not invoked") because it can't show how the actual value differed. ArgumentCaptor instead *captures* the real argument so you assert on it afterward with your normal assertion library, yielding rich failure messages and letting you inspect captured values across invocations. Rule of thumb: use argThat for simple boolean conditions or when you must match a specific call among several; use a captor when you want detailed, readable assertions on the object. Also keep argThat predicates side-effect-free and remember it still obeys the all-arguments-are-matchers rule.
code
java · 14 lines// argThat: inline custom predicate (partial match on fields)
verify(orderRepo).save(argThat(o -> o.getStatus() == Status.PAID
&& o.getTotal().compareTo(BigDecimal.TEN) > 0));
// Mixing rule still applies: wrap literals in eq()
verify(emailService).send(eq("[email protected]"),
argThat(body -> body.contains("receipt")));
// ArgumentCaptor: capture then assert with rich diagnostics
ArgumentCaptor<Order> captor = ArgumentCaptor.forClass(Order.class);
verify(orderRepo).save(captor.capture());
Order saved = captor.getValue();
assertThat(saved.getStatus()).isEqualTo(Status.PAID);
assertThat(saved.getTotal()).isGreaterThan(BigDecimal.TEN);go deeper
Knows argThat() takes a condition and ArgumentCaptor grabs the real argument, even if unsure when to pick each.
Writes correct argThat predicates and basic captor verifications, and respects the mixing rule with eq().
Articulates the diagnostics/stubbing/multi-call trade-offs, keeps predicates pure, and improves argThat output with a custom ArgumentMatcher toString.
Establishes team guidance (argThat for selection, captor for inspection), spots flaky tests from side-effecting predicates, and reviews tests for brittle equals-based matching that should be partial.
## The need: matching part of an object Sometimes a method takes a rich object and you only care about a few of its fields. Suppose `notificationService.send(EmailMessage msg)` and you want to assert it was called with a message **to** a particular address, ignoring subject and body. Exact-value matching (`eq(message)`) would force you to build an identical `EmailMessage` and rely on its `equals`. That's brittle. You want a *partial* match. ## argThat — custom matchers `ArgumentMatchers.argThat(ArgumentMatcher<T> matcher)` lets you supply your own rule. `ArgumentMatcher<T>` is a functional interface with one method `boolean matches(T argument)`, so you pass a lambda: ```java verify(notificationService).send(argThat(m -> m.getTo().equals("[email protected]"))); ``` Mockito evaluates the predicate against each actual argument; the call matches when it returns `true`. Because it is a matcher, it lives on the same thread-local stack as `any()`/`eq()` and obeys the **all-or-nothing** mixing rule (wrap any other literal arguments in `eq()`). Variants you'll meet: - `argThat(...)` — generic object matcher. - `intThat(...)`, `longThat(...)`, `charThat(...)` etc. — primitive-typed versions that avoid autoboxing-null pitfalls. - You can also implement `ArgumentMatcher<T>` as a named class and override `toString()` so the failure message describes the expectation (e.g. "order with status PAID"). This is the main lever to improve argThat's otherwise weak diagnostics. ## ArgumentCaptor — capture then assert An `ArgumentCaptor<T>` is the alternative tool. Instead of judging match/no-match inline, it **records** the actual argument so you can assert on it afterward: ```java ArgumentCaptor<EmailMessage> captor = ArgumentCaptor.forClass(EmailMessage.class); verify(notificationService).send(captor.capture()); EmailMessage sent = captor.getValue(); // the actual object assertThat(sent.getTo()).isEqualTo("[email protected]"); // rich assertion, rich failure ``` `captor.capture()` is itself a matcher (so it also obeys the mixing rule). `getValue()` returns the last captured argument; `getAllValues()` returns every captured argument across multiple matching invocations. ## The trade-offs, in detail **Diagnostics.** This is the biggest difference. When `argThat` fails, the predicate only returned `false`; Mockito can't show *why*, so you get a generic "Wanted but not invoked" / "Argument(s) are different" message with no field-level detail (unless you wrote a custom `ArgumentMatcher` with a descriptive `toString`). A captor + a normal assertion library (AssertJ/JUnit) produces a precise message like "expected to:'[email protected]' but was:'[email protected]'". For complex objects, captors win on debuggability. **Stubbing vs verification.** `argThat` works in **both** `when()` stubbing and `verify()`. A captor is really only meaningful in **verification** — capturing during stubbing is discouraged because stubs may run zero or many times, making the captured value misleading. **Multiple calls / re-matching.** `argThat` re-evaluates its predicate on every call, so it naturally selects matching calls among many. A captor instead records *all* of them; you then index into `getAllValues()`. Pick based on whether you want to *select* a call (argThat) or *inspect* a sequence (captor). **Side effects.** Keep `argThat` predicates pure — Mockito may invoke them multiple times and at surprising moments; mutating state inside the predicate causes flaky tests. **Readability.** A one-line boolean condition reads better inline as `argThat`. Several assertions on a captured object read better as a captor with explicit assert statements. ## Rule of thumb - Simple boolean condition, or you must pick one call out of several to stub/verify → **argThat** (add a custom `toString` if you need good failure output). - You want rich, readable field-level assertions, or to inspect the captured value/sequence → **ArgumentCaptor**. ## Bottom line `argThat` is the flexible, inline custom matcher; `ArgumentCaptor` is the capture-and-assert tool with superior diagnostics. They overlap, but you choose based on whether the test needs *matching logic* or *post-hoc inspection*.
- Your argThat verification fails with an unhelpful message. How do you improve the diagnostics without switching to a captor?Replace the lambda with a named class implementing ArgumentMatcher<T> and override toString() to describe the expectation (e.g. 'order with status PAID and total > 10'); Mockito prints that in the failure. Or just switch to a captor for field-level assertion messages.
- Can you use ArgumentCaptor to assert the order of arguments across multiple calls?Yes — verify(mock, times(n)).method(captor.capture()) then captor.getAllValues() returns every captured argument in invocation order, which you assert on as a list.
saying these in an interview costs you the question
- Using ArgumentCaptor during stubbing — capture belongs in verification.
- Putting side effects inside an argThat predicate (Mockito may call it multiple times).
- Forgetting eq() on other arguments once you use argThat.
- Expecting argThat to print field-level diffs without a custom ArgumentMatcher toString.
- Reaching for argThat to do many assertions where a captor reads far better.