skip to content

Answers API

answers { } computes the result from the actual invocation, giving access to arguments and call metadata. Interviewers ask about it because dynamic stubs separate people who know the library from people who memorized 'returns'.

on this pageshow

explore

questions

5

Inside MockK's `answers { }` block, how do you read the arguments the caller actually passed, and what are `firstArg()`, `arg<T>(n)` and `lastArg()` really doing?

level: middleimportance: must knowfreq 50%

answer

  1. answers { } runs per call, at call time
  2. firstArg/secondArg/thirdArg/lastArg + arg<T>(n), ZERO-based
  3. reified T = unchecked cast ⇒ ClassCastException at call time
  4. echo stub: answers { firstArg() }
  5. raw args list + nArgs for generic answers

basics

~20 s

The block runs at call time with an answer scope exposing the invocation. Use firstArg<T>(), secondArg<T>(), thirdArg<T>(), lastArg<T>() or arg<T>(n) for a zero-based index. Each is an indexed read of the argument list plus an unchecked cast to T, so a wrong T fails at that line.

solid answer

~50 s

`answers { }` registers a lambda that MockK invokes **on every matching call**, with a receiver — the answer scope — that carries the invocation. From it you read the arguments: ```kotlin every { formatter.format(any(), any()) } answers { "${firstArg<String>()}:${secondArg<Int>()}" } ``` `firstArg`, `secondArg`, `thirdArg` and `lastArg` are conveniences over `arg<T>(n)`, which is **zero-based**: `firstArg()` is `arg(0)`. All of them do the same two things — index into the recorded argument list, then cast to the reified type parameter. The cast is unchecked at the DSL level, so specifying the wrong type produces a ClassCastException at the moment the answer runs, inside the code under test, which can look like a production bug until you read the trace. The scope also exposes the raw `args` list and `nArgs`. Use the typed accessors by default; drop to `args` only for genuinely generic answers. This is how you build pass-through stubs — `answers { firstArg() }` — and answers computed from input rather than fixed.

code

kotlin · 9 lines
kotlin
val repo = mockk<UserRepo>()
every { repo.save(any()) } answers { firstArg() }   // returns what it was given

val rates = mockk<RateTable>()
every { rates.lookup(any(), any()) } answers {
    val from = firstArg<String>()
    val to = secondArg<String>()
    if (from == to) 1.0 else 0.9
}

go deeper

for a junior

Show you can write an echo stub with answers { firstArg() } and know the block runs on each call with the real arguments.

for a middle

Name the full accessor set including arg<T>(n) and its zero-based indexing, and explain that the type parameter is a cast applied at call time.

for a senior

Talk about the failure modes — ClassCastException surfacing inside the code under test, nullable arguments, silent off-by-one — and about choosing answers versus returns versus capture.

for a principal

Set a norm for how much logic belongs in a stub at all, and why assertions inside answer blocks are dangerous when production code catches broadly.

## What an answer block is Every terminator on a MockK stub registers an *answer*: an object that produces a result for an invocation. `returns x` registers a constant one. `answers { … }` registers a **lambda** one — code that MockK executes each time a matching call arrives. Two consequences that people miss: - The block runs **at call time**, not at stub-definition time. It sees the actual arguments of the actual call, and it re-runs for every matching call. - The block's value is the return value of the mocked function, so its type must fit the function's return type. ## The receiver: the answer scope The lambda has a receiver object (MockK's answer scope) which wraps the invocation and exposes it in typed form. The argument surface is: | Accessor | Meaning | |---|---| | `arg<T>(n)` | argument at **zero-based** index `n`, cast to `T` | | `firstArg<T>()` | `arg<T>(0)` | | `secondArg<T>()` | `arg<T>(1)` | | `thirdArg<T>()` | `arg<T>(2)` | | `lastArg<T>()` | the final argument, cast to `T` | | `args` | the raw `List<Any?>` of arguments | | `nArgs` | how many arguments there are | The type parameter is reified and applied as a cast. Kotlin will often infer it from context — `answers { firstArg() }` compiles when the function returns `String` — so you can omit it, at the price of a less obvious failure if the inference is wrong. Writing it explicitly, `firstArg<String>()`, documents the intent. ## The failure mode to know ```kotlin every { repo.save(any()) } answers { firstArg<Long>() } // arg is actually a User ``` Nothing complains at stub time. The exception fires the first time production code calls `save`, on the line inside the answer block, and it surfaces as a `ClassCastException` propagating out of the mocked call. Candidates who have not hit this waste real time believing the code under test is broken. The same applies to index mistakes: `arg` is zero-based, so `arg<Int>(1)` is the **second** parameter. Off-by-one here yields either a class-cast failure or, worse, a plausible-but-wrong value when adjacent parameters share a type. A nullable argument must be read as a nullable type — `firstArg<String?>()` — or the cast to the non-null type fails when the caller passed null. ## What it is good for **Pass-through / echo stubs.** The commonest use by far. A repository whose `save` returns the entity it was given: ```kotlin every { repo.save(any()) } answers { firstArg() } ``` This keeps the test honest: whatever the service builds flows back, so downstream assertions inspect the real object rather than a hard-coded stand-in. **Input-dependent answers** where a matcher-per-value would be noisy: ```kotlin every { rates.for(any()) } answers { if (firstArg<String>() == "USD") 1.0 else 0.9 } ``` **Invoking a callback the caller passed**, when the collaborator's contract is "call me back": ```kotlin every { client.load(any(), any()) } answers { secondArg<(String) -> Unit>().invoke("payload") } ``` **Side effects for recording**, e.g. appending to a list the test owns — a lighter alternative to a capture slot when you only need the values. ## When *not* to use it If the answer is constant, `returns` says so more clearly. If you only want to *inspect* what was passed, argument capture is the purpose-built tool and keeps assertions out of the stub. And avoid putting test assertions inside the block: an assertion that fails there throws from inside the code under test, where a `try/catch` in production code may swallow it and turn a real failure into a green test. ## Relationship to the rest of the answer API The same block form appears as `andThenAnswer { }` for later entries in a sequence, and as `coAnswers { }` when the stubbed function is suspending and the block itself needs to suspend. The scope and its accessors are identical in all three. ## Checklist - Runs per call, at call time, with the real arguments. - `arg(n)` is zero-based; `firstArg` = index 0; `lastArg` = final argument. - The type parameter is an unchecked cast — wrong type ⇒ ClassCastException inside the test subject. - Nullable arguments need a nullable type parameter. - Prefer `returns` for constants, capture for inspection, `answers` for computation.

  • A stub uses firstArg<Long>() but the parameter is a User. When and how does that fail?
    Not at stub time — the DSL only records a lambda. It fails the first time the mocked function is called, with a ClassCastException raised on that line inside the answer block and propagating out of the mocked call into the code under test. It commonly gets misread as a production bug until you notice the frame is inside the test's answer lambda.
  • How is an answers block different from capturing an argument with a slot?
    An answers block computes the return value at call time and can use the arguments to do it; a capture stores the argument so the test can assert on it afterwards. Use capture when the goal is inspection, because assertions inside a stub run inside the code under test and can be swallowed by a catch block there. Use answers when the return value genuinely depends on the input.
  • Does arg(1) mean the first or the second parameter?
    The second. arg is zero-based, so arg(0) is equivalent to firstArg() and arg(1) to secondArg(). Off-by-one errors here are especially nasty when neighbouring parameters share a type, because the cast succeeds and the stub silently returns a value derived from the wrong argument.

saying these in an interview costs you the question

  • Believing the answers block is evaluated once when the stub is defined.
  • Treating arg(n) as one-based.
  • Assuming the type parameter is checked, so a wrong type would be caught at compile or stub time.
  • Reading a nullable argument with a non-null type parameter.
  • Putting assertions inside the answer block instead of capturing and asserting outside.

context

open as a page

In MockK, how does `andThenAnswer` differ from `andThen`, and when do you need a computed answer for the second and later calls of the same stub?

level: middleimportance: should knowfreq 28%

basics

~20 s

andThen appends a constant value to the sequence; andThenAnswer appends a lambda that runs at call time with the answer scope, so it can read the arguments, compute a result or call the original. Use it whenever a later step must depend on that call's input, not on a value known up front.

open as a page

What does MockK's `callOriginal()` do inside an answer block, on which kinds of mocks does it work, and why would you use it instead of simply leaving the function unstubbed?

level: seniorimportance: should knowfreq 35%

basics

~20 s

callOriginal() invokes the real implementation behind the intercepted call and returns its result. It needs an original to call — a spy, or a real object/class/static/constructor that MockK has patched — and fails on a plain interface mock. Use it to keep real behavior while still recording, wrapping or overriding the call conditionally.

open as a page

A team's MockK stubs have grown into `answers { }` blocks with branching, mutable state and assertions inside them. How do you judge when answer-block logic has gone too far, and what do you do about it?

level: principalimportance: should knowfreq 22%

basics

~20 s

Answer blocks should compute a return value from the call, nothing more. Branching on the invocation, accumulating state, or asserting inside the block means the stub has become an untested implementation living in a lambda. Replace it with a small hand-written double, and move assertions to captures outside.

open as a page

Beyond the arguments, what invocation metadata does MockK expose inside an `answers { }` block, and when is reaching for `call`, `self` or `nArgs` actually justified?

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

The answer scope exposes the whole invocation: call, call.invocation (with self, method and args), plus shortcuts args and nArgs. Justified uses are answers shared across overloads or several mocks, and diagnostics. If a stub needs the invoking object's identity to decide, that is usually a smell.

open as a page