A teammate wants to reduce duplication in Mockito tests by storing an argument matcher in a field or returning one from a helper method and reusing it across several verifications. Explain what goes wrong and what a safe refactoring looks like.
answer
- Matcher call = side effect + throwaway dummy
- Never assign a matcher to a variable or field
- Extract the call (helper method), not the result
- Reusable artefact = ArgumentMatcher predicate
- Registration order is positional - no lazy/conditional calls
basics
~20 sA matcher is not a reusable value - the call registers it on a thread-local stack and returns a dummy. Storing it in a field registers once at initialisation and yields a dummy afterwards, breaking matcher counts. Extract a helper that returns the matcher expression, and call it inline at each use site.
solid answer
~40 sMatcher factory calls have an effect at the moment of invocation: `any()` pushes a matcher and returns `null`. So: ```java ArgumentMatcher<User> m = any(); // registers once, m is null verify(repo).save(m); // no matcher recorded now -> counts wrong ``` The field holds a dummy, not a reusable predicate, and the registration happened at the wrong time - typically leaving the stack dirty and producing InvalidUseOfMatchersException in an unrelated place. The safe shape is to extract a **method that performs the matcher call**, so each call site re-registers: ```java private static User anyActiveUser() { return argThat(u -> u.isActive()); } verify(repo).save(anyActiveUser()); ``` Or extract the reusable **predicate** as an `ArgumentMatcher<User>` implementation and wrap it at each use: `argThat(ACTIVE_USER)`. What must never be hoisted is the `argThat(...)`/`any()` invocation itself.
code
java · 11 lines// BROKEN: registers at field init, then passes null
private final User ANY_USER = any();
verify(repo).save(ANY_USER);
// SAFE 1: helper performs the matcher call at each use site
static User activeUser() { return argThat(User::isActive); }
verify(repo).save(activeUser());
// SAFE 2: share the predicate, wrap it inline
static final ArgumentMatcher<User> ACTIVE = u -> u != null && u.isActive();
verify(repo).save(argThat(ACTIVE));go deeper
Say plainly that matcher calls cannot be stored in variables because they return dummies, and that the helper must contain the matcher call.
Explain registration order and positional pairing, and give both safe extractions - a helper method that calls the matcher, and a shared ArgumentMatcher wrapped in argThat at each site.
Weigh readability against diagnostics: shared predicates hide field-level detail, so prefer captor-and-assert for rich objects and named matcher classes with toString() when a predicate is genuinely shared.
Set the convention: matcher expressions live only inside mocked call argument lists, shared matching logic is expressed as named ArgumentMatcher types, and verification of complex payloads standardises on capture-and-assert for failure-message quality.
## What a matcher expression really is Every Mockito matcher factory - `any()`, `eq()`, `isNull()`, `argThat(...)` - has a dual nature: 1. **Side effect**: it pushes an `ArgumentMatcher` onto a thread-local stack. 2. **Return value**: a type default (`null`, `0`, `false`) whose only purpose is to satisfy the compiler at the call site. The return value is worthless. Treating it as a value - assigning it, storing it in a field, passing it around - keeps the worthless half and throws away the useful half, because the registration already happened at assignment time. ## The failure modes **Field initialisation.** A `private final User ANY_USER = any();` in a test class registers a matcher when the instance is constructed - i.e. once per test instance, outside any stubbing - and leaves it on the stack. The next mock invocation sees a stray matcher and blows up with `InvalidUseOfMatchersException`, often in a later, innocent-looking test method. **Reuse across verifications.** Even if the timing happened to work once, a second `verify(repo).save(ANY_USER)` records zero matchers, so Mockito compares the dummy `null` with `equals` and the verification silently fails to match - or, mixed with other matchers, throws on the count. **Conditional or lazy evaluation.** Putting a matcher inside an `if`, a ternary, a lambda body executed later, or a stream operation makes registration order unpredictable relative to the mock call. Matchers pair *positionally in registration order*, so anything that reorders or defers evaluation corrupts the pairing. **Nested calls.** `verify(repo).save(builder.withId(anyLong()).build())` registers a matcher for an argument of a *non-mock* builder; the mock invocation then has one argument and one stray matcher. ## The safe refactorings **1. Extract the call, not the result.** A static helper whose body performs the matcher call is fine, because the helper is invoked inline at each use site and therefore registers exactly once, in the right position: ```java static User activeUser() { return argThat(User::isActive); } verify(repo).save(activeUser()); verify(audit).record(eq("save"), activeUser()); ``` The helper must return the matcher call's dummy directly and be called *inside* the mock invocation's argument list. Naming it like a matcher (`activeUser()`, `anyPositiveId()`) signals that it must not be hoisted. **2. Extract the predicate.** The genuinely reusable artefact is the `ArgumentMatcher` implementation, not the registration: ```java static final ArgumentMatcher<User> ACTIVE = u -> u != null && u.isActive(); verify(repo).save(argThat(ACTIVE)); ``` This is the cleanest option when several tests share non-trivial matching logic, and it keeps `argThat(...)` at the call site where it belongs. It also allows a named class with a readable `toString()`, which improves Mockito's failure output. **3. Prefer capture-and-assert for complex objects.** When the duplication is really about asserting many fields, an `ArgumentCaptor` plus ordinary assertions removes the need for shared matchers entirely and gives a far better failure message than a boolean predicate that just says "argument did not match". ## Guardrails - Rule of thumb: a matcher expression must appear **lexically inside** the argument list of the mocked call it belongs to, or inside a helper that is itself called there. - Never assign a matcher call to a variable, even temporarily for readability. - Never call a matcher inside an assertion, a data builder, or a lambda deferred to another thread; the stack is thread-local. - Enable `MockitoExtension` so any accidental leak is reported on the offending test rather than the next one. ## Interview framing This question separates candidates who know the rule from candidates who know the model. The expected answer names the dual nature of matcher calls, explains that the returned dummy is not reusable, and offers the two legitimate extractions: a helper that *calls* the matcher, or a shared `ArgumentMatcher` predicate wrapped in `argThat` at each site.
- Is it safe to call a matcher inside a lambda passed to verify, for example in a loop that verifies several calls?It is safe only if the lambda executes synchronously on the test thread at the moment the mock invocation is made, so registration happens immediately before interception. Any deferral - an executor, a stream collected later, a callback - breaks the ordering or lands the matcher on a different thread, where the thread-local storage is separate. Keep matcher calls in straight-line code inside the argument list.
- How would you make a shared custom matcher produce readable failure messages?Implement ArgumentMatcher in a named class rather than a lambda and override toString() to describe the expectation, for example 'active user with role ADMIN'. Mockito prints that description in the verification failure, so the report says what was expected instead of showing an opaque lambda reference. For field-level detail, prefer an ArgumentCaptor and assertion library output.
saying these in an interview costs you the question
- Treating a matcher call as a value object that can be stored and reused
- Thinking the problem is only stylistic rather than a real registration-order bug
- Wrapping the field access in eq() to 'balance the counts', which just compares against null
- Putting matcher calls inside conditionals or loops that may register a different number of matchers than the call has arguments
- Assuming a matcher registered on one thread is visible to a mock invoked on another