Mockito's built-in matchers cannot express a condition like 'an Order whose total exceeds 100 and whose status is PENDING'. How do you match an argument on an arbitrary condition when stubbing or verifying with Mockito?
answer
- argThat(ArgumentMatcher) - boolean matches(T)
- functional interface -> lambda
- pure predicate, no assertions inside
- primitives: intThat / longThat / doubleThat
- AdditionalMatchers: gt, lt, and, or, not
basics
~20 sUse argThat(ArgumentMatcher) with a predicate: verify(repo).save(argThat(o -> o.total() > 100 && o.status() == PENDING)). ArgumentMatcher is a functional interface with one boolean matches(T) method, so a lambda works. It composes with the built-in matchers in the same call.
solid answer
~50 sargThat(ArgumentMatcher<T>) is the extension point. ArgumentMatcher is a single-method functional interface - boolean matches(T argument) - so a lambda or a small named class both work, and the matcher is used identically in stubbing and verification. Two things to keep in mind. The predicate must be pure: Mockito may call it more than once, for every recorded invocation and again while building a failure message, so no side effects and no assertions inside it - a throwing matcher derails Mockito's invocation matching and yields a confusing error. Second, a lambda's toString() is meaningless, so a failed verification prints something like Test$$Lambda$14@6d06d69c; a named class overriding toString() makes the report readable. Mockito also ships AdditionalMatchers - gt, lt, geq, leq, and, or, not, aryEq - which cover common numeric and boolean combinations without writing a predicate at all.
code
java · 9 linesimport static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.*;
verify(repo).save(argThat(o ->
o.total().compareTo(new BigDecimal("100")) > 0
&& o.status() == Status.PENDING));
when(pricing.quote(argThat(r -> r.items().size() > 10)))
.thenReturn(bulkQuote);go deeper
Show argThat with a lambda and say ArgumentMatcher has one boolean matches(T) method.
Add the purity rules, the primitive variants, and that it works in stubbing to discriminate between calls.
Discuss failure diagnosability and when a predicate is the wrong tool because it collapses the reason for failure to a boolean.
Frame reusable named domain matchers as shared test vocabulary, and set the boundary between selecting an invocation and asserting its content.
## The API org.mockito.ArgumentMatcher<T> declares one method: boolean matches(T argument). ArgumentMatchers.argThat(ArgumentMatcher<T>) registers it and returns a dummy value, exactly like any() or eq(). Because the interface is functional, the common form is a lambda: verify(repo).save(argThat(o -> o.total().compareTo(HUNDRED) > 0 && o.status() == PENDING)); It works in stubbing too: when(pricing.quote(argThat(r -> r.items().size() > 10))).thenReturn(bulkQuote). That is a capability plain assertions do not have - a matcher can make two stubs on the same method behave differently depending on the argument, and can pick out which of several recorded invocations you meant. ## Rules the predicate must obey Mockito calls matches() while comparing the recorded invocations, and may call it repeatedly - once per candidate invocation, and again when composing a verification failure report. Therefore: - No side effects. Do not mutate the argument, record state, or count calls inside the matcher. - No assertions. Throwing from matches() does not produce a nice assertion failure; it escapes in the middle of Mockito's matching machinery and usually surfaces as an unrelated-looking error. If you want rich assertion messages, assert on the value outside the verification instead. - Handle unexpected input. In stubbing the matcher can see arguments from calls you did not anticipate, including null. A predicate that dereferences blindly will NullPointerException on an unrelated call. ## Primitive parameters argThat is generic and returns null, so using it directly for a primitive parameter throws NullPointerException during unboxing. Mockito ships primitive-typed siblings for exactly this: intThat, longThat, shortThat, byteThat, charThat, floatThat, doubleThat, booleanThat, each taking an ArgumentMatcher of the boxed type and returning a safe default. ## AdditionalMatchers before writing your own org.mockito.AdditionalMatchers covers a lot of what people hand-roll: gt, lt, geq, leq and cmpEq for Comparable and numeric arguments, eq(double, delta) for floating point tolerance, aryEq for arrays, find(regex) for strings, and the combinators and, or, not that compose two matchers on the same argument. verify(meter).record(and(gt(0L), lt(1000L))) reads better than a predicate and produces a better message. ## Named matchers over lambdas A lambda is fine for a one-off. When the same condition appears in several tests, a small named class - class PendingOrderOver implements ArgumentMatcher<Order>, with a meaningful toString() - is worth the few lines: it names the domain concept, is reusable, and gives a readable failure message instead of a synthetic lambda class name. ## Where it does not fit If what you actually want is a rich assertion over the argument - several fields, a diff on failure, soft assertions - a predicate is the wrong shape, because it collapses everything to a boolean and loses the explanation. Use a predicate matcher when you need to select or discriminate an invocation; use extracted-value assertions when you need to describe what was wrong.
- Is it acceptable to put assertions inside an argThat predicate so you get a good failure message?No. Mockito may invoke the matcher several times, including while building its own failure report, and an exception thrown from matches() escapes into the matching machinery rather than being reported as a clean assertion failure. Keep the predicate a pure boolean and put rich assertions outside the verification.
- Can argThat be used in stubbing as well as verification, and does that change anything?Yes, it works in both, and stubbing is where it is most powerful because you can give the same method different answers depending on the argument. The extra care needed is that the predicate will be evaluated against every call to that method, including unexpected or null arguments, so it must be null-safe and side-effect free.
saying these in an interview costs you the question
- Putting assertThat or assertions inside the matcher predicate
- Assuming the predicate runs exactly once per call
- Dereferencing the argument without a null check in a stubbing matcher
- Using argThat directly on a primitive parameter and being surprised by the NPE
- Hand-rolling range predicates when AdditionalMatchers.gt/lt exist