skip to content

Matchers and Capture

How MockK decides whether a stub or verification matches a call, and how to pull real argument values out for assertions. Interviewers lean on matcher rules because mixing them wrong produces the library's most confusing failures.

on this pageshow

explore

questions

17

Beyond `any()` and `eq()`, what argument matchers does MockK offer, and how do you choose between them when stubbing or verifying a call?

level: middleimportance: must knowfreq 60%

answer

  1. literal == eq under the hood
  2. any() also matches null
  3. isNull() / isNull(inverse = true)
  4. less/more/range need Comparable
  5. and / or / not compose matchers

basics

~10 s

MockK adds neq, isNull() and isNull(inverse = true), ofType<T>(), less/more (with andEquals) and range for Comparables, match { } predicates, the and/or/not combinators, and refEq/nrefEq/cmpEq for reference or compareTo equality.

solid answer

~50 s

The catalog splits into families: - **Constant**: `any()` matches anything, including null. - **Equality**: `eq(value)`, `neq(value)`, plus `refEq`/`nrefEq` for reference identity and `cmpEq` for `compareTo`-based equality. A bare literal in a recording block is implicitly `eq`. - **Nullability**: `isNull()`, and `isNull(inverse = true)` for "not null". - **Type**: `ofType<T>()` matches by the argument's runtime type — handy for sealed hierarchies and overloads. - **Ordering** (needs `Comparable`): `less(v)`, `more(v)`, each with `andEquals = true`, and `range(from, to, fromInclusive, toInclusive)`. - **Predicate**: `match { it.total > 0 }`, `matchNullable { }` for nullable parameters, `coMatch { }` for suspending predicates. - **Combinators**: `and(a, b)`, `or(a, b)`, `not(m)`. Choose the most specific matcher that still expresses intent: `eq` when the exact value is the point, `ofType`/`range` when a class of values is, `any()` when the argument is genuinely irrelevant. Over-loose matchers make green tests meaningless; over-tight ones make them brittle.

code

kotlin · 6 lines
kotlin
every { repo.find(ofType<Uuid>()) } returns user
every { rates.between(range(1, 10), any()) } returns 0.5
every { audit.log(match { it.actor == "admin" }) } just Runs

verify { repo.save(neq(User.EMPTY)) }
verify { cache.put(isNull(inverse = true), any()) }

go deeper

for a junior

Name a few beyond any()/eq() — isNull, ofType, range — and know that a bare literal means equality.

for a middle

Walk the families, note that any() also matches null, and explain choosing the most specific matcher that still expresses the test's claim.

for a senior

Add erasure limits on ofType, the readability advantage of ordering matchers over predicates, and how over-loose matchers produce green but meaningless tests.

for a principal

Frame matcher precision as a suite-wide property: too loose and tests stop catching regressions, too tight and every refactor churns the suite.

## Why matchers exist Inside `every { }` and `verify { }` MockK is *recording*, not calling. Each argument position is stored as a **matcher** — a predicate over the value that will arrive at call time. A plain literal is not special-cased magic: MockK wraps it as an equality matcher. So `every { svc.find(7) }` is `every { svc.find(eq(7)) }`. Everything else in the catalog is a different predicate in that same slot. ## The catalog by family **Constant.** `any()` matches every argument. It is a constant-true predicate, which is why it also matches `null` on nullable parameters — a detail that surprises people who expect "any non-null value". **Equality.** `eq(value)` is the default. `neq(value)` matches everything except the given value. `refEq(value)` compares by reference identity rather than `equals`, and `nrefEq(value)` is its negation. `cmpEq(value)` compares with `compareTo`, so it matches values that are "equal enough" for ordering even when `equals` disagrees. `eq` also accepts an `inverse` flag, which is another way to spell `neq`. **Nullability.** `isNull()` matches only null; `isNull(inverse = true)` matches only non-null. Both are more precise than reaching for `any()` and hoping. **Type.** `ofType<T>()` (or `ofType(SomeClass::class)`) matches when the argument is an instance of the given type. It shines with sealed classes and polymorphic events: `every { handler.on(ofType<PaymentFailed>()) } returns Unit`. Because it inspects the runtime class, generic type arguments are erased — `ofType<List<String>>()` really only asserts "is a List". **Ordering.** For `Comparable` arguments: `less(v)`, `more(v)`, each accepting `andEquals = true` to include the bound, and `range(from, to, fromInclusive = true, toInclusive = true)` for an interval. These express intent far better than a `match { }` lambda doing the same arithmetic, and they print readably in failure output. **Predicate.** `match { it.userId == 7L }` runs your lambda against the incoming argument. `matchNullable { }` is the variant whose lambda receives a nullable value, needed when the parameter type itself is nullable. `coMatch { }` exists for predicates that must suspend. Predicates are the universal fallback — and the least self-describing, because the failure report can only show that *a* predicate did not match. **Combinators.** `and(left, right)`, `or(left, right)` and `not(matcher)` compose matchers, so `and(more(0), less(100))` is legal, as is `not(isNull())`. Two more families belong to neighbouring concerns: capture matchers store the argument for later assertion, and vararg matchers deal with variadic parameters. ## Choosing The governing question is *what does this test actually claim?* - If the exact value is the contract — an id, an amount, a status — use `eq` (or just the literal). A test that says `any()` where the value matters passes for a bug that sends the wrong id. - If a **class** of values is the contract, name that class: `ofType<Retryable>()`, `more(0)`, `range(1, 10)`. This is the sweet spot — it documents intent and does not break when an unrelated field changes. - If the argument is genuinely irrelevant to this assertion, `any()` is honest and better than an over-specified value that will churn. - Reach for `match { }` only when nothing simpler fits, and prefer to extract it into a named extension so the intent has a name. ## Practical notes - Matchers are recorded per argument position; a call being matched has to satisfy all of them. - The same catalog works in `every`, `coEvery`, `verify`, `coVerify` and the ordering verifications — matchers are a property of the recording model, not of stubbing specifically. - Matchers only mean anything inside those recording blocks; computing one outside and passing it in does not work, and mixing raw literals with matchers in the same call has its own rule. - When a stubbed call "doesn't fire", the fastest diagnosis is usually that a matcher was too tight: an `eq` on an object whose class has no `equals`, or an `ofType` defeated by erasure. ## A worked example ```kotlin every { audit.log(ofType<SecurityEvent>(), more(0L)) } just Runs verify { audit.log(match { it.actor == "admin" }, any()) } ``` The stub says "any security event with a positive timestamp", and the verification says "one of them came from admin, timestamp irrelevant" — two different precisions on the same method, each chosen to match what the test claims.

  • Does MockK's `any()` match a null argument?
    Yes. `any()` is a constant-true matcher, so on a nullable parameter it accepts null just as readily as a value. If the test means "some non-null argument", the correct matcher is `isNull(inverse = true)`, and if it means "null specifically", it is `isNull()`.
  • When would you use `ofType<T>()` rather than `any()`?
    When the method takes a supertype and the test is about one concrete subtype — sealed event hierarchies are the classic case. `ofType<PaymentFailed>()` documents that the stub applies only to that branch and lets you register a different answer for siblings. Remember it checks the runtime class, so generic parameters are erased.

saying these in an interview costs you the question

  • Believing MockK only offers any() and eq().
  • Assuming any() excludes null.
  • Using match { } for everything instead of the specific ordering or type matchers.
  • Thinking ofType<T>() checks generic type arguments.
  • Claiming matchers are only usable in every, not in verify.

context

open as a page

In MockK, why can matcher functions such as any() or match { } only be written inside an every, coEvery or verify block? Explain what actually happens at compile time and at runtime when one of them is called.

level: middleimportance: must knowfreq 45%

basics

~20 s

Matchers are declared on MockK's MockKMatcherScope, the receiver of every/verify blocks, so elsewhere they do not compile. Inside, a call like any() registers a matcher with MockK's call recorder and returns a generated dummy value the recorder binds back to that argument position.

open as a page

A MockK test captures a collaborator's argument with a CapturingSlot, but the mocked method is invoked several times during the test. Which value does the slot hold when the test asserts, and how would you keep every call's argument instead?

level: middleimportance: must knowfreq 45%

basics

~20 s

A CapturingSlot holds one value: each matching call overwrites it, so you end up with the last call's argument. To keep them all, capture into a MutableList — capture(list) appends every matching call's argument in call order.

open as a page

You are stubbing a MockK mock of an interface method declared as fun log(vararg parts: String). Why does every { log.log(any()) } fail to match a call that passes three arguments, and what does MockK give you to match a call with any number of vararg elements?

level: middleimportance: must knowfreq 35%

basics

~20 s

In the every block each value you write in a vararg position records one element matcher, so any() describes a call with exactly one element. Use MockK's anyVararg() spread into the call — every { log.log(*anyVararg()) } — to match any number of elements.

open as a page

How does MockK's `eq()` decide whether an argument matches, and when would you reach for `refEq()` or `cmpEq()` instead?

level: middleimportance: should knowfreq 45%

basics

~10 s

eq() compares structurally: arrays are compared by content, everything else by equals(). refEq() compares by reference identity, and cmpEq() compares with compareTo, which matters for types like BigDecimal where equals and compareTo disagree.

open as a page

In MockK, what happens when one argument of a stubbed call is a matcher such as any() and another is a plain literal like 42? When does that combination break, and why does wrapping the literal in eq() fix it?

level: middleimportance: should knowfreq 40%

basics

~20 s

MockK treats a non-matcher argument as an implicit eq(literal), so mixing usually works. It breaks when MockK cannot tell a generated signature value apart from your literal — likeliest for tiny value spaces such as Boolean. Wrapping every argument in eq() makes matchers and arguments line up exactly.

open as a page

MockK provides varargAll and varargAny for constraining the elements of a vararg parameter. What does each one assert about the arguments, and what does the predicate block expose besides the element value itself?

level: middleimportance: should knowfreq 25%

basics

~20 s

varargAll matches when every element it covers satisfies the predicate; varargAny matches when at least one does. Inside the block, it is the element, and the scope also exposes position (the element's index in the vararg) and nArgs (how many vararg arguments there were).

open as a page

In MockK, how do you express "this argument must be null", "must not be null", and "must be this concrete subtype" — and what are the limits of type-based matching?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Use isNull() for null and isNull(inverse = true) for not-null; any() will not do, since it matches null too. Use ofType<T>() for a concrete subtype — it checks the runtime class, so generic arguments are erased.

open as a page

MockK's `match { }` lets you supply an arbitrary predicate as an argument matcher. What are the mechanics and the downsides, and how do you keep predicate-based matching debuggable?

level: seniorimportance: should knowfreq 35%

basics

~20 s

match { } stores your lambda as the matcher and runs it against each candidate argument at match time. The downside is diagnostics: a failing lambda prints as an opaque matcher. Keep predicates small, pure, and named via reusable extensions, or use specific matchers instead.

open as a page

A Kotlin test fails with `io.mockk.MockKException: Missing calls inside every { ... } block.` What does MockK mean by that, and how would you diagnose it systematically?

level: seniorimportance: should knowfreq 35%

basics

~20 s

It means MockK's recorder saw no mockable call inside the block. Usually the target is not a mock, the call was resolved statically (top-level or extension function, an unmocked static), it hit a real object returned by the mock, or the block only computed values without invoking the mock.

open as a page

A collaborator you are mocking takes a lambda parameter — something like retry(times: Int, block: () -> String). Using MockK, how do you get hold of that lambda and actually run it, and what is the difference between doing it inside an answers block and capturing it into a slot for later?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Use MockK's captureLambda() in the every block and invoke it from the answers block via lambda<() -> String>().invoke(). To fire it later instead, capture it into a normal slot — slot<() -> String>() with capture(slot) — and call slot.captured() from the test after the call returns.

open as a page

A MockK test hands the same MutableList to capture(...) in both an every block and a later verify block. It ends up with twice as many captured arguments as there were real calls. Explain when MockK writes into a capture target, and how you would structure the test instead.

level: seniorimportance: should knowfreq 30%

basics

~20 s

Capture is a side effect of matching, so it fires wherever matching happens: inside every it fires when production calls the mock, and inside verify it fires again as MockK re-matches the recorded calls. The same list in both gets each argument twice. Capture in one place only.

open as a page

A MockK test captures a collaborator's argument into a slot, but the assertion on slot.captured fails even though the production code demonstrably passed the right data. Given how capture stores what it sees, what are the likely causes and how do you get a trustworthy snapshot?

level: seniorimportance: should knowfreq 28%

basics

~20 s

A slot stores the reference, not a copy. If production mutates, clears or reuses that object after the call, the slot shows the later state. Take a snapshot inside an answers block (copy the argument) instead of capturing the live object — or check whether a later call simply overwrote the slot.

open as a page

Using MockK, can one stub pin the first vararg elements to exact values while constraining the remaining ones with a predicate — for example, first two elements exactly 5 and 6, everything after that equal to 7? How is that written and what does it match?

level: seniorimportance: should knowfreq 20%

basics

~20 s

Yes. Write the literal elements first and spread a vararg matcher after them: every { calc.manyMany(5, 6, *varargAll { it == 7 }) } returns 3. The literals pin those positions element-wise; the spread matcher covers the remaining elements.

open as a page

When a mocked call takes a large request object, how do you decide between matching it with `eq` on the whole object, a `match { }` predicate on a few fields, or `any()` — and what policy would you set for a team?

level: principalimportance: should knowfreq 25%

basics

~20 s

Match on exactly what the test claims. eq on the whole object when the object is the contract and has real value equality; a narrow predicate when only some fields are the claim; any() when the argument is irrelevant here and asserted elsewhere. Never let looseness be accidental.

open as a page

MockK evaluates the lambda passed to every or verify under a call recorder, and may execute it more than once while it resolves the call signature. Given that, how would you factor shared stubbing and verification helpers across a large Kotlin test suite?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Treat recording blocks as pure descriptions: one mock call, no side effects, nothing built inside them. Share matchers as MockKMatcherScope extension functions called inside the block, and share whole stubs as ordinary functions that wrap the entire every/returns statement.

open as a page

Code under test calls a mocked collaborator from several threads (or from parallel coroutines), and the MockK test captures those arguments to assert on them. What can go wrong with the captured data, and how would you design the test so the assertions are dependable?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

A CapturingSlot keeps only a nondeterministic last value, and a plain ArrayList handed to capture in an every block is appended from many threads with no synchronisation. Let all work finish, then capture inside verify, and assert on the set rather than the order.

open as a page