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?
answer
- non-matcher arg = implicit eq()
- binding by generated signature values
- Boolean/tiny value space = ambiguous
- 'left matchers: [...]' names the unplaced matcher
- all-matchers habit: wrap constants in eq()
basics
~20 sMockK 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.
solid answer
~50 sUnlike an all-or-nothing rule, MockK binds matchers to positions by **signature values**: each matcher returns a generated dummy, and after the recorded call MockK maps arguments equal to those dummies onto the matchers, treating every remaining argument as an implicit `eq(...)`. So a mixed call like `repo.find(any(), 42)` normally records fine. It breaks when the mapping is ambiguous — when the type has too few distinguishable values to generate a unique dummy (`Boolean` is the classic case) or when your literal happens to collide with the generated value. Then you get `Failed matching mocking signature for ...` with a `left matchers: [...]` line listing matchers MockK could not place, or, worse, a stub that silently binds the matcher to the wrong parameter. The deterministic fix is to make the call **all matchers**: wrap the literals in `eq(42)`. Now the matcher count equals the argument count and MockK does not have to infer anything. That is why teams adopt the habit of all-matchers-or-all-literals, never one of each.
code
kotlin · 9 lines// usually fine: 42 is recorded as an implicit eq(42)
every { repo.find(any(), 42) } returns row
// safest form: every argument is a matcher
every { repo.find(any(), eq(42)) } returns row
// risky: a matcher mixed with a boolean literal has a tiny value space
// every { repo.search(any(), true) } returns rows
every { repo.search(any(), eq(true)) } returns rowsgo deeper
Know and apply the habit: if one argument needs a matcher, wrap the others in eq() so the whole call is matchers.
Explain the mechanism — implicit eq() for non-matcher arguments, matchers bound to positions by generated signature values — and when that inference becomes ambiguous.
Diagnose from symptoms: read 'Failed matching mocking signature ... left matchers', suspect tiny value spaces such as Boolean, and separate a mis-bound matcher from an equals()-related mismatch.
Turn it into a convention: all-matchers calls in shared stubbing helpers, explicit match { } for arguments whose equality semantics are uncertain, so failures always mean the code changed rather than the DSL misfired.
## What actually happens with a mixed call When you write `every { repo.find(any(), 42) } returns row`, MockK executes the lambda under a call recorder. `any()` registers a matcher and returns a generated *signature value* of the parameter type; `42` is just the number 42. When the mocked method is invoked, MockK holds the argument list `[<signature value>, 42]` and the matcher list `[any()]`, and reconciles them: arguments that are recognisably generated signature values mark matcher positions; every other argument is wrapped as an implicit `eq(...)`. The recorded stub is effectively `find(any(), eq(42))`. So the honest answer to "can you mix matchers and constants in MockK?" is **usually yes** — the binding is positional-by-value, not a rule that forbids mixing. ## Where the model runs out of room The inference depends on the generated signature value being distinguishable from anything you might legitimately pass: - **Tiny value spaces.** A `Boolean` parameter has two possible values. There is no dummy that cannot also be a real literal, so a call mixing a matcher with a boolean literal — or with several booleans — is prone to ambiguous binding. The same pressure exists for very small enums and, less often, for tiny numeric literals. - **Collisions.** If the literal you pass happens to equal the generated value for a matcher of the same type, MockK cannot tell which position was the matcher. - **Values you carry around.** Reusing a matcher's returned value elsewhere in the block, or passing the same instance to two parameters, feeds the reconciliation values it cannot interpret. The visible symptoms are the exception `io.mockk.MockKException: Failed matching mocking signature for ...` followed by `left matchers: [any()]` — MockK telling you which matchers it could not place — or, more insidiously, a stub that records successfully but with the matcher attached to the wrong parameter, so the stub never matches at call time and you debug a `no answer found for: ...` (strict mock) or an unexpected relaxed default instead. ## Why eq() is the fix `eq(42)` is a real matcher: it registers with the recorder and returns its own signature value. If every argument is a matcher, the matcher count equals the argument count and each position is accounted for — there is nothing left to infer, and the tiny-value-space problem disappears because the boolean argument is now itself a matcher slot rather than a literal MockK must recognise. That is where the rule of thumb comes from: **all matchers or all literals, never a mix**. It is not that MockK forbids mixing; it is that all-matchers is the form with no inference in it, so it never fails for reasons unrelated to your test. ## The related trap: what implicit eq() means An implicit or explicit `eq(x)` matches with `equals()`, not identity. For Kotlin data classes that is structural equality, which is usually what you want; for a class that does not override `equals()` it degrades to reference equality, and a stub written with a freshly constructed argument silently never matches. When a mixed call misbehaves, check both things: is the matcher bound to the position you meant, and does the literal's type actually implement value equality? If it does not, `refEq(...)` or `match { }` expresses the intent explicitly. ## How to talk about it A strong answer separates the *rule of thumb* from the *mechanism*. The rule of thumb — wrap constants in `eq()` so a call is all matchers — is what you follow every day and what makes reviews easy. The mechanism — signature-value binding with implicit `eq()` for the rest — is what lets you explain why a mixed call worked fine in ten tests and blew up in the eleventh with `left matchers: [any()]`, and why the eleventh one had a `Boolean` parameter. Candidates who state "MockK forbids mixing matchers and constants" are repeating a rule imported from other mocking libraries and cannot explain any of the observed behaviour.
- A stub records without error but never matches at call time. How does matcher/literal binding explain that?The recording can succeed while the matcher is bound to the wrong argument position, so the stub describes a call your code never makes. The other common cause is the implicit eq(): it matches with equals(), so a literal of a class without a value-based equals() only matches the identical instance. Rewrite the call with explicit matchers on every argument and, where equality is doubtful, use match { } to state the condition.
- Does eq() compare by equality or by identity, and what is the alternative?eq() uses equals(), which is structural for data classes and reference-based for classes that do not override it. MockK also offers refEq() for deliberate reference comparison and match { } for an arbitrary predicate. Choosing consciously matters most for arguments the production code constructs internally, where you never hold the instance the mock will receive.
saying these in an interview costs you the question
- "MockK forbids mixing matchers and constants" stated as an absolute, imported from another library
- Thinking a literal argument is ignored rather than recorded as an implicit eq()
- Not recognising the 'left matchers: [...]' line as MockK naming the matchers it could not bind
- Assuming eq() compares by identity, or that it works for classes with no equals() override
- Explaining a mismatch as "MockK is flaky" instead of checking matcher-to-position binding