MockK exposes an infix DSL written as `mock getProperty "speed"` and `mock setProperty "speed" value 33`. What problem does it solve, what does it require, and what does it cost you?
answer
- private property → no expression to record
- mock getProperty "name"
- setProperty "name" value matcher
- spyk(obj, recordPrivateCalls = true)
- string name = rename-unsafe, untyped
basics
~20 sIt reaches properties the test cannot name in code — private ones — by string, reflectively. On a spy you need spyk(obj, recordPrivateCalls = true) for private access to be recorded. Cost: no compile-time safety, rename-unsafe, untyped results.
solid answer
~50 sNormal property stubbing (`every { mock.prop }`) needs the property to be *visible* from the test source. For a `private` property there is no expression you can write, so MockK provides a reflective, string-named DSL inside recording blocks: ```kotlin every { mock getProperty "speed" } returns 33 every { mock setProperty "speed" value less(50) } just Runs verify { mock getProperty "speed" } ``` Matchers work in the `value` position, so setter constraints stay expressive. Two requirements matter. First, for a spy over a real object, private accesses are only recorded if you create it with `spyk(obj, recordPrivateCalls = true)` — otherwise the internal call is invisible to verification. Second, the property name is a **string**: renaming the property leaves the test compiling and silently wrong, and the result is untyped so you cast. Use it for legacy or third-party classes you cannot change. Where you own the code, widening the property to `internal` and testing through the public surface is the better fix.
code
kotlin · 11 linesclass Car {
private var speed: Int = 0
fun accelerate() { speed += 10 }
fun isFast() = speed > 50
}
val car = spyk(Car(), recordPrivateCalls = true)
every { car getProperty "speed" } returns 90
assertTrue(car.isFast())
verify { car getProperty "speed" }go deeper
Know that it exists for properties the test cannot name, and that the normal every { mock.prop } form is the one you use day to day.
Show the syntax for get and set, note that matchers work in the value position, and mention the string name is not refactor-safe.
Add the recordPrivateCalls = true requirement on spies and argue the encapsulation cost — brittle coupling to storage rather than behaviour.
Set the policy: reflective access is a scaffold for legacy or third-party code with an exit plan, not a pattern the team reaches for on code it owns.
## The gap it fills MockK's normal property stubbing is *expression-based*: `every { car.speed } returns 33` works because the test can write `car.speed`. Kotlin visibility decides whether that expression compiles. `private` members are invisible outside their class, and `internal` members are invisible outside the module, so for those there is simply no expression to record. MockK's answer is a reflective escape hatch that identifies the member by name. ## The DSL Inside `every { }`, `coEvery { }` or `verify { }` blocks: ```kotlin every { mock getProperty "speed" } returns 33 every { mock setProperty "acceleration" value less(5) } just Runs verify { mock getProperty "speed" } verify { mock setProperty "acceleration" value less(5) } ``` `getProperty` records a getter invocation by name; `setProperty ... value ...` records a setter invocation, and the `value` position accepts ordinary argument matchers, so you can constrain which assignments you are stubbing or verifying. It belongs to the same family as MockK's dynamic method calls (`invoke "name" withArguments listOf(...)`, `invokeNoArgs "name"`), which exist for the same visibility reason. ## The `recordPrivateCalls` requirement The DSL is most often used against a **spy** over a real object, because that is where private state actually lives. By default a spy does not record calls the object makes to its own private members — recording every internal hop would be noisy and would change how much of the object's behaviour is observable. Passing `recordPrivateCalls = true` at construction turns that recording on: ```kotlin val car = spyk(Car(), recordPrivateCalls = true) every { car getProperty "speed" } returns 90 ``` Without the flag, a stub or verification on a private property has nothing to bind to and the test fails in a confusing way — a *no answer found* or a verification that never sees the call. On a pure `mockk<T>()` the flag is not the issue; the issue is only whether the member exists on the mocked type. ## What it costs 1. **No compile-time checking.** `"speed"` is a string literal. Rename the property in the production class and the test still compiles; it fails at runtime, or worse, silently stops matching what you think it matches. 2. **Refactoring tools do not follow it.** An IDE rename will not update the literal unless it happens to do string search, so the test drifts away from the code. 3. **Untyped results.** The recorded expression is not statically typed to the property's type, so you end up asserting on or casting an `Any?`-shaped value, losing the compiler's help. 4. **It cements encapsulation breaches.** A test that asserts on a private field is coupled to *how* the class stores things, not what it does. Any internal refactor that keeps behaviour identical can still break the test — the classic definition of a brittle test. 5. **It is easy to over-reach.** Once the hatch is open, teams start verifying private methods too, and the test suite becomes a second, worse copy of the implementation. ## When it is nonetheless the right call - **Legacy code you cannot restructure yet.** You need a characterisation test before refactoring; reaching into private state is a temporary scaffold that you remove once behaviour is pinned down. - **Third-party classes.** You do not own the visibility, and the only lever is reflective. - **A private property that is genuinely the observable outcome** in a class whose public surface reveals nothing — for example a cached handle whose lifecycle is the point of the test. Even then, prefer exposing the fact through a public query if you own the class. ## Better alternatives when you own the code Widen the property to `internal` so the test source set of the same module can see it (Kotlin's `internal` plus a test-only-usage annotation is a common convention), inject the collaborator that holds the state instead of hiding it, or restructure so the behaviour is observable through the public API. Each of these converts a reflective, name-based coupling into a compiled one. The rule of thumb: reach for `getProperty`/`setProperty` when you cannot change the class, not when you cannot be bothered to.
- You own the class under test. What would you do instead of `getProperty`?Change visibility or design rather than the test. Widening the property to `internal` lets the module's tests see it with full type safety, and injecting the state-holding collaborator makes it observable without reflection at all. Best of all is exposing the outcome through the public API, so the test asserts behaviour instead of storage.
- The `value` position in `setProperty "x" value ...` — can you use matchers there?Yes, it takes an ordinary argument matcher, so `value less(5)` or `value any()` are both valid. That keeps setter stubs and verifications as expressive as normal parameter matching, which matters because you often want to verify only that *some* assignment happened.
saying these in an interview costs you the question
- Believing MockK can stub private members through the normal expression DSL.
- Forgetting `recordPrivateCalls = true` and concluding MockK cannot see private access at all.
- Treating the string name as refactor-safe.
- Presenting reflective property access as good default practice rather than an escape hatch.
- Confusing it with mocking private *methods* and inventing an API name for that.