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?
answer
- literal head + spread matcher tail
- 5, 6, *varargAll { it == 7 }
- positions are exact — order matters
- empty tail satisfies varargAll vacuously
- known call → write the literals instead
basics
~20 sYes. 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.
solid answer
~50 sMockK records vararg arguments element-wise, so literals and a spread matcher can sit in one call. `every { calc.manyMany(5, 6, *varargAll { it == 7 }) } returns 3` matches a call whose first vararg element is 5, whose second is 6, and whose remaining elements all equal 7 — `manyMany(5, 6, 7)` and `manyMany(5, 6, 7, 7)` match, while `manyMany(5, 7, 7)` and `manyMany(5, 6, 7, 8)` do not. The idiom is: literal prefix elements, then one spread matcher standing for the tail. If you need finer control over the tail, the predicate scope gives you `position` (the element's index in the vararg) and `nArgs` (the number of vararg arguments), so `varargAny { position == 2 && it == 7 }` pins a specific later index and an `nArgs` condition constrains the length. Read it as a shape declaration: fixed head, quantified tail.
code
kotlin · 7 linesinterface Calc { fun manyMany(vararg x: Int): Int }
val calc = mockk<Calc>()
every { calc.manyMany(5, 6, *varargAll { it == 7 }) } returns 3
calc.manyMany(5, 6, 7, 7) // -> 3
// calc.manyMany(5, 6, 7, 8) // no match: tail predicate failsgo deeper
Know that literals and a spread matcher can appear in the same vararg call, with the literals pinning the leading positions.
Explain the element-wise recording that makes it work, name what matches and what does not, and use position/nArgs for finer tail control.
Choose the form deliberately — literals when the call is known, mixed head-plus-tail when the payload is genuinely variable — and debug non-matching stubs by arity, order, quantifier and the empty-tail case.
Weigh diagnosability: predicate-heavy vararg expectations pass and fail opaquely, so establish where in the suite they are worth their cost versus asserting exact calls or observable outcomes.
## Why mixing is possible at all MockK records what you write inside `every { }` / `verify { }` as a list of matchers positioned against the real invocation. For a vararg parameter, each expression you place in that position contributes to the element-wise description of the vararg. A concrete value contributes an equality expectation for its position; a spread vararg matcher contributes a rule for the elements it covers. Because both are just entries in the same element-wise description, they compose in one call. ## The shape ```kotlin interface Calc { fun manyMany(vararg x: Int): Int } every { calc.manyMany(5, 6, *varargAll { it == 7 }) } returns 3 ``` Read as: element 0 is exactly 5, element 1 is exactly 6, and every remaining element satisfies `it == 7`. Matching calls: `manyMany(5, 6, 7)`, `manyMany(5, 6, 7, 7, 7)`. Non-matching: `manyMany(5, 7, 7)` (position 1 is wrong), `manyMany(5, 6, 7, 8)` (a tail element fails the predicate), `manyMany(6, 5, 7)` (order matters — positions are not a set). The convention is literal prefix first, spread matcher for the tail. That mirrors how varargs are usually meaningful in practice: a fixed head that identifies the operation, followed by a variable-length payload. ## Constraining the tail more precisely The predicate scope exposes more than the element: - `position` — the element's index in the vararg, so you can pin a specific later index without writing out everything before it: `varargAny { position == 3 && it == 0 }`. - `nArgs` — the number of vararg arguments, so you can bound the length: a predicate including `nArgs == 4` only matches calls of that shape. And remember the quantifier choice: `varargAll` requires every covered element to satisfy the predicate, `varargAny` only requires one. Mixing a literal head with `varargAny` is a much weaker statement than it looks — "first two are 5 and 6, and *some* later element is 7" will accept calls carrying plenty of unexpected values. ## Interaction with the rest of the signature If the method has fixed parameters before the vararg, they follow the ordinary matcher rules and are written as usual: `every { log.log(any(), "start", *anyVararg()) }` matches any level, a first vararg element equal to `"start"`, and any tail. The vararg matcher never absorbs the non-vararg parameters. ## Vacuity, again `varargAll` over an empty tail is vacuously satisfied. In the mixed form that means `manyMany(5, 6)` — literals matched, no tail elements at all — satisfies `varargAll { it == 7 }` for the tail. If the test means "and at least one 7 followed", say so with an `nArgs` condition rather than assuming the universal predicate implies existence. ## When to prefer plain literals If you know the exact call, write it out: `verify { calc.manyMany(5, 6, 7) }` is unambiguous and produces the best failure message, because MockK can show the expected and actual arguments rather than reporting that an opaque predicate did not match. The mixed form earns its place when the head is fixed and the tail is genuinely variable in length or content — a command plus a batch of ids, an event name plus a variable set of tags. That is a real pattern, and expressing it any other way (a matcher per possible arity, or an over-broad `anyVararg()` that asserts nothing about the payload) is worse. ## Debugging checklist for a mixed vararg stub that will not match 1. Count the elements: literal prefix + tail must be reachable by the real call's arity. 2. Check ordering — positions are exact, not a set. 3. Check the quantifier — did you mean all or any? 4. Check the tail predicate against the actual values, especially the boundary case of an empty tail. 5. If the mock is relaxed, remember a mismatch is silent; temporarily make it strict to see the "no answer found" message with the real arguments.
- Does `every { calc.manyMany(5, 6, *varargAll { it == 7 }) }` match the call `manyMany(5, 6)` with no further arguments?Yes — the literal positions match and the universal predicate over an empty tail has nothing to contradict it. If the test means the call must carry at least one further element, express that explicitly with an `nArgs` condition in the predicate rather than relying on `varargAll` to imply existence.
- How would you pin a later vararg position without writing out all the elements before it?Use the `position` property inside the predicate — for instance `varargAny { position == 3 && it == 0 }` constrains index 3 specifically. This keeps the earlier elements unconstrained while still making a positional claim, which is exactly what the scope's positional information exists for.
saying these in an interview costs you the question
- Believing MockK forbids mixing concrete vararg values with matchers in one call
- Treating vararg positions as an unordered set, so 6, 5 is expected to match 5, 6
- Assuming a literal head plus varargAny is as strong a statement as a literal head plus varargAll
- Expecting the spread matcher to also cover fixed parameters that precede the vararg
- Reaching for the mixed form when the exact call is known, and then fighting an unhelpful failure message