Explain MockK argument matchers any() and eq(). When must you use a matcher, and what is the rule about mixing matchers with literals?
answer
- any() = anything; eq(x) = equals x
- literal == eq(literal)
- all-or-nothing: one matcher means all matchers
- matchers only inside every/verify
- eq(x, inverse=true), refEq, ofType, more/less
basics
~20 sany() matches any argument; eq(x) matches a value equal to x. If you use a matcher for one argument in a call, you must use matchers for all arguments of that call — you can't mix matchers with plain values.
solid answer
~40 sMatchers tell MockK how to decide whether a recorded call matches an actual call. any() accepts any value of the parameter type; eq(value) matches by equals(). Passing a plain literal like 1L is implicitly treated as eq(1L). The all-or-nothing rule: within a single call inside every/verify, if you use even one matcher you must express every argument as a matcher — mixing a raw literal with any() throws. Other built-ins include eq(x, inverse = true) for not-equal, refEq for reference equality, ofType<T>() for type matching, range/comparison matchers (more(), less()), and isNull()/cmpEq. Matchers are only valid inside the every/verify DSL; they record matcher objects, they don't return real values. The same matcher set works for stubbing and verification. For capturing argument values you use slot()/capture(), which is the natural next step.
code
kotlin · 6 lines// Mixing a literal with a matcher is illegal:
// every { repo.save("Ada", any()) } // BAD
// Wrap every argument:
every { repo.save(eq("Ada"), any()) } returns Unit
verify { repo.save(any(), more(0)) }go deeper
Knows any() matches anything and eq() matches a specific value.
States the all-or-nothing rule and that a literal equals eq(literal); knows matchers are DSL-only.
Chooses matcher strictness deliberately and knows refEq/ofType/inverse/comparison matchers.
Frames matcher precision as a brittleness-vs-coverage tradeoff and sets team conventions for it.
## What matchers are When you write `every { repo.findById(1L) }`, MockK must decide which *actual* calls this stub matches. A **matcher** is the rule for that decision. MockK records matcher objects while evaluating the DSL lambda. ## The two core matchers - `any()` — matches **any** argument of the parameter's type, including null for nullable types. Use `any<String>()` when the type can't be inferred. - `eq(value)` — matches when the actual argument is `equals()` to `value`. A plain literal is sugar for `eq`: `findById(1L)` is the same as `findById(eq(1L))`. ```kotlin every { repo.findById(any()) } returns null // any id every { repo.findById(eq(1L)) } returns User(1L) // exactly 1L every { repo.findById(1L) } returns User(1L) // same as eq(1L) ``` ## The all-or-nothing rule This is the most-tested gotcha. **Within one call, if you use any matcher, every argument must be a matcher.** Mixing breaks MockK's recording: ```kotlin // WRONG: name is a literal, age is a matcher every { repo.save("Ada", any()) } // throws / misbehaves // RIGHT: wrap the literal too every { repo.save(eq("Ada"), any()) } ``` Reason: matcher recording is positional and stateful; a raw literal isn't recorded as a matcher, so MockK can't line them up. ## Useful relatives of any()/eq() - `eq(x, inverse = true)` — matches anything **not** equal to x. - `refEq(x)` — reference (identity) equality instead of `equals`. - `ofType<T>()` — matches by runtime type. - `more(x)`, `less(x)`, `range(...)` — comparison-based matching for Comparable types. - `isNull()` / `nullable` variants — null handling. ## Matchers work in both DSLs The identical matcher set is valid in `every` (stubbing) and `verify` (verification): ```kotlin verify { repo.save(eq("Ada"), any()) } ``` ## Important constraints - Matchers are **only** legal inside `every`/`verify` lambdas. Calling `any()` in normal code returns a placeholder, not a real value. - You cannot store a matcher in a variable and reuse it across calls in a way that breaks positional recording. ## Why it matters Matchers control test precision: `any()` keeps tests loose and resilient; `eq()` pins exact contracts. Choosing the right strictness is a design decision — over-tight matchers make brittle tests, over-loose ones miss bugs.
- Why does every { repo.save("Ada", any()) } fail?It mixes a raw literal with a matcher; MockK requires every argument to be a matcher once any matcher is used, so wrap "Ada" in eq().
- How do you match 'any value except 5'?Use eq(5, inverse = true), which matches anything not equal to 5.
saying these in an interview costs you the question
- Mixing a literal argument with any() in the same call
- Calling any() outside an every/verify lambda expecting a real value
- Thinking eq uses reference equality (it uses equals())
- Believing matchers differ between every and verify
- Always using any() so tests never assert the right arguments