skip to content

Compare returns, returnsMany, throws, and answers { } as stub terminators in MockK. When is answers { } the right choice?

level: seniorimportance: nice to knowfreq 45%

answer

  1. returns = constant
  2. returnsMany / andThen = sequence
  3. throws = error path
  4. answers { } = compute from args (firstArg/arg<T>)
  5. pick simplest terminator that fits

basics

~20 s

returns gives one fixed value, returnsMany gives values in sequence, throws makes the call fail, and answers { } computes the result with a lambda. Use answers when the return must depend on the call's arguments.

solid answer

~50 s

These are the terminators that close an every { } block. returns x yields x on every call. returnsMany(listOf(a, b, c)) yields a, then b, then c across successive calls (repeating the last when exhausted, depending on version). throws ex raises ex. answers { } runs a lambda for each call, giving full control: it can read arguments with firstArg(), secondArg(), arg<T>(i), or lastArg(), inspect the original call via the implicit call receiver, and return a computed value. Use answers when the result is argument-dependent (echo an id, transform input), when you need side effects, or to vary behaviour per call beyond a fixed sequence. andThen chains multiple answers for sequential dynamic behaviour. For coroutines you'd use coEvery/coAnswers (a sibling topic). Prefer the simplest terminator that expresses intent: returns for constants, returnsMany for a known sequence, answers only when logic is genuinely needed.

code

kotlin · 7 lines
kotlin
every { repo.count() } returns 5
every { repo.count() } returnsMany listOf(0, 1, 2)
every { repo.findById(any()) } throws IllegalStateException("down")
every { repo.findById(any()) } answers {
    val id = firstArg<Long>()
    if (id > 0) User(id, "x") else null
}

go deeper

for a junior

Knows returns gives a fixed value and throws raises an error.

for a middle

Uses returnsMany for sequences and knows answers exists for dynamic results.

for a senior

Reads arguments via firstArg/arg<T>() in answers, chains with andThen/andThenAnswers, and picks the simplest terminator.

for a principal

Sets guidance that stubs stay declarative — discourages logic-heavy answers blocks that hide real behaviour under test.

## What a terminator is An `every { call }` block must end with a **terminator** that says what the stubbed call does. MockK offers several. ## `returns` — constant value ```kotlin every { repo.count() } returns 5 // always 5 ``` ## `returnsMany` — a sequence Returns successive values across calls. Useful for simulating pagination or state changes. ```kotlin every { repo.count() } returnsMany listOf(0, 1, 2) // 1st call -> 0, 2nd -> 1, 3rd -> 2 ``` Equivalent chaining: `returns 0 andThen 1 andThen 2`. ## `throws` — raise an exception ```kotlin every { repo.findById(any()) } throws IllegalStateException("db down") ``` Use to test error paths. `andThenThrows` can chain. ## `answers { }` — computed result The most powerful terminator. The lambda runs on each call and can read the actual arguments: ```kotlin every { repo.findById(any()) } answers { val id = firstArg<Long>() // or arg<Long>(0) if (id == 1L) User(1L, "Ada") else null } ``` Argument accessors inside `answers`: - `firstArg<T>()`, `secondArg<T>()`, `thirdArg<T>()`, `lastArg<T>()` - `arg<T>(index)` for arbitrary positions - `args` for the full list, and `invocation`/`call` for call metadata `answers` can also produce side effects or echo captured slots: ```kotlin val s = slot<User>() every { repo.save(capture(s)) } answers { s.captured.copy(id = 42L) } ``` ## Chaining dynamic behaviour `andThenAnswers { }` chains multiple answer lambdas for per-call variation beyond a static list. ## Choosing the right one - Constant result -> `returns`. - Known finite sequence -> `returnsMany` / `andThen`. - Error path -> `throws`. - Result depends on arguments, needs computation, or echoes input -> `answers`. Prefer the simplest terminator that expresses intent; reaching for `answers` when `returns` suffices makes tests harder to read. ## Note on coroutines Suspend functions need `coEvery { } coAnswers { }` — that's the coroutine-mocking sibling topic, not covered here.

  • How do you read the second argument inside an answers block?
    Use secondArg<T>() or arg<T>(1); args gives the whole argument list.
  • What's the difference between returnsMany and andThen chaining?
    They're equivalent for sequential constants; returnsMany takes a list, andThen chains values (or answers) one at a time and can mix in andThenThrows/andThenAnswers.

saying these in an interview costs you the question

  • Using answers { } where a simple returns would do
  • Thinking returns can vary per call
  • Not knowing firstArg/arg<T>() to read arguments in answers
  • Trying to use answers for suspend functions (needs coAnswers)
  • Believing throws can't be chained with andThenThrows

context