skip to content

In MockK, how do you stub and verify a Kotlin property on a mock — both a read-only `val` and an assignable `var` — and what is MockK actually intercepting when you do?

level: middleimportance: must knowfreq 55%

answer

  1. property = getter/setter calls, no field
  2. assignment needs an answer too
  3. justRun { mock.prop = any() }
  4. verify { mock.prop } / verify { mock.prop = 5 }
  5. @JvmField / Java field = no accessor to intercept

basics

~20 s

MockK intercepts accessor calls, not fields. Stub reads with every { mock.prop } returns x. An assignment is a setter call needing an answer, so use justRun { mock.prop = any() }, and assert with verify { mock.prop = 5 }.

solid answer

~50 s

A Kotlin property on the JVM is a backing field plus a getter, and for `var` a setter. A mock has no backing field, so MockK intercepts the **accessor calls**. Inside `every { }`, writing `mock.prop` records a getter invocation, so `every { mock.prop } returns "x"` is just a stub of a no-arg method. Assignment is the mirror image: `mock.prop = 5` records a setter call returning `Unit`. On a strict mock it still needs an answer, so it fails unless stubbed — `justRun { mock.prop = any() }`, `every { mock.prop = any() } just Runs`, or a mock created with unit-function relaxation. Verification uses the same shapes: `verify { mock.prop }` asserts the property was read, `verify { mock.prop = 5 }` asserts it was assigned, and matchers work in the value position. The trap: a mock is stateless. Assigning `mock.prop = 5` does not change what the getter returns — the getter keeps answering with whatever you stubbed.

code

kotlin · 13 lines
kotlin
interface Session {
    val id: String
    var lastSeen: Long
}

val session = mockk<Session>()
every { session.id } returns "s-1"
justRun { session.lastSeen = any() }

service.touch(session)

verify { session.id }
verify { session.lastSeen = more(0L) }

go deeper

for a junior

Be able to write every { mock.prop } returns value and know that assigning to a var on a mock also has to be stubbed.

for a middle

Explain that properties are intercepted as getter/setter calls, show justRun { mock.prop = any() } and verify { mock.prop = 5 }, and name the stateless-mock trap.

for a senior

Add the boundaries: no accessor means no interception (@JvmField, Java fields), spies run the real accessor, and read-your-writes needs answers { } or a fake rather than a mock.

for a principal

Frame it as a design signal — tests that need stateful properties on mocks usually want a real object or a fake, and you set that as the team default.

## What a Kotlin property compiles to A `val name: String` becomes a private backing field plus a `getName()` accessor; a `var` adds `setName(value)`. Kotlin source reads `obj.name` and writes `obj.name = x`, but the bytecode calls accessors. That is the whole basis of property stubbing in MockK: a mock instance has **no state and no backing field**, so there is nothing to read — MockK intercepts the accessor invocation and answers it like any other method call. ## Stubbing a getter ```kotlin every { session.id } returns "s-1" ``` Inside the `every` lambda the mock is in recording mode, so `session.id` is not a field read; it is a recorded call to `getId()`. The stub is an ordinary zero-argument stub, which is why everything that works for methods works here: `returns`, `returnsMany`, `throws`, `answers { }`. This works for interfaces and for final Kotlin classes alike, because MockK instruments classes rather than generating subclass proxies. The one place it breaks is when there is no accessor at all: a Java `public` field, or a Kotlin property annotated `@JvmField`, is read as a raw field access, and a field read cannot be intercepted. If a test needs to control such a value, you have to set it on a real instance instead of mocking it. ## Stubbing a setter `mock.lastSeen = 42` records a call to `setLastSeen(42)`. It returns `Unit`, but a strict mock does not treat `Unit` as "nothing to answer" — every intercepted call needs a registered answer, so an unstubbed assignment fails with a *no answer found* error naming the setter. Three ways out: - `justRun { mock.lastSeen = any() }` - `every { mock.lastSeen = any() } just Runs` - create the mock so unit-returning functions are relaxed Matchers apply to the assigned value exactly as they would to a parameter, so `justRun { mock.lastSeen = more(0L) }` is legal and constrains which assignments are stubbed. ## Verifying property access `verify { mock.id }` asserts the getter was called — useful when the property read *is* the interaction under test. `verify { mock.lastSeen = 42 }` asserts the assignment happened with that value; `verify { mock.lastSeen = any() }` asserts only that something was written. Because both are just calls in the recording model, all the usual verification shapes apply to them. ## Mocks are stateless — the number-one surprise ```kotlin val m = mockk<Counter>() every { m.value } returns 0 justRun { m.value = any() } m.value = 7 m.value // still 0 ``` There is no field behind `value`, and the setter stub does not feed the getter. If the test genuinely needs read-your-writes behaviour, you have three honest options: keep a local variable and wire it with `answers { }` on the getter plus a capturing setter stub; use a spy over a real instance so the real accessors run; or use a small hand-written fake. Reaching for a stateful mock is usually a sign the collaborator should have been a real object in this test. ## Properties with logic, and lateinit A custom getter that computes something is still just a getter to MockK — the computation is replaced wholesale by your stub. On a spy the original getter runs unless stubbed. A `lateinit var` on a mock is likewise never initialised: the getter is intercepted before any initialisation check, so a stub is required and no `UninitializedPropertyAccessException` appears. ## Private and inaccessible properties If the property is not visible from the test source set you cannot write `mock.prop` at all; MockK offers a reflective, string-named DSL for that case (`getProperty` / `setProperty`), which trades compile-time safety for reach. ## Failure modes to recognise - *no answer found for: Session(#1).getId()* → getter never stubbed (or a different mock instance is in play). - *no answer found for: ...setLastSeen(...)* → the code assigns a property you forgot to stub. - A stub that "doesn't take" on a `var` → you stubbed the setter and expected the getter to follow.

  • Why does assigning to a `var` on a strict MockK mock throw, when the setter returns Unit?
    MockK does not special-case the return type: every intercepted call must resolve to a registered answer, and a setter is an intercepted call like any other. `Unit` is a value it would have to produce, not an absence of one. Relaxation for unit-returning functions (or `justRun`) is what registers the do-nothing answer.
  • A property on a Kotlin class is annotated `@JvmField`. Can MockK stub it?
    No. `@JvmField` removes the accessors and exposes a plain field, and a field read compiles to a direct field access with no method call to intercept. The test has to use a real instance whose field is set, or the class has to keep normal accessors.

saying these in an interview costs you the question

  • Thinking MockK stubs the backing field rather than the accessor.
  • Expecting a mock to remember an assigned value and return it from the getter.
  • Believing unit-returning setters need no stub on a strict mock.
  • Claiming property stubbing only works on interfaces or open classes.
  • Assuming `lateinit` on a mock must be initialised before it can be read.

context