skip to content

Creating Mocks

Creating mocks with mockk<T>() and what makes that possible for final Kotlin classes. Interviewers ask about creation options because they reveal whether you know what a MockK mock actually is under the hood.

on this pageshow

explore

questions

5

MockK can create a mock of a final Kotlin class without you marking it `open` or extracting an interface. Explain the instrumentation mechanism that makes that possible, and what it means for how the mock instance itself is produced.

level: middleimportance: must knowfreq 55%

answer

  1. agent rewrites bytecode, not a subclass
  2. final is irrelevant — hook is inside the method
  3. Objenesis: no constructor, no init block
  4. ByteBuddy on JVM, DexMaker on Android
  5. no call site → inline/static/constructor need other tools

basics

~20 s

MockK ships a JVM agent that rewrites class bytecode in place, inserting a dispatch hook into method bodies — so final classes and final methods are interceptable. The instance itself is allocated without running any constructor, then every call is routed to MockK's stub registry.

solid answer

~50 s

Kotlin classes and members are **final by default**, so a mocking library built on subclass proxies cannot override them — that is where the "make it `open` just for tests" smell comes from. MockK avoids proxies: `mockk<T>()` uses the MockK agent (ByteBuddy-based on the JVM, DexMaker on Android) to **transform the class's own bytecode**, adding a hook at the top of method bodies that asks MockK's dispatcher whether this call is being recorded or stubbed. Because the hook lives inside the real class, `final`, `internal` and `object` members are all interceptable. The instance is created with **Objenesis**-style allocation: no constructor, no `init` block, no property initializers run. Fields stay at JVM defaults, which is fine because every method call is intercepted before it can read them. Consequences: no `open` keyword or interface just for testability; but anything with no virtual call to hook — inline functions, top-level/static functions, constructors — needs a different tool (`mockkStatic`, `mockkConstructor`).

code

kotlin · 9 lines
kotlin
class PaymentGateway(private val http: HttpClient) {   // final by default
    init { http.warmUp() }                              // never runs for a mock
    fun charge(cents: Long): String = http.post("/charge", cents)
}

val gateway = mockk<PaymentGateway>()   // no `open`, no interface, no constructor call
every { gateway.charge(500) } returns "ok"

assertEquals("ok", gateway.charge(500))

go deeper

for a junior

Know the headline: MockK mocks final Kotlin classes without open or an extracted interface, unlike proxy-based mockers. Be able to say it uses a JVM agent.

for a middle

Explain the mechanism: agent-based bytecode instrumentation inserts a dispatch hook inside the real methods, so final is irrelevant, and the instance is allocated without running a constructor.

for a senior

Add the boundaries and operational detail — inline/static/constructor cases have no dispatch to hook, and the dynamic agent attach produces a JDK 21+ warning you should recognise in CI output.

for a principal

Frame it as design leverage: because testability no longer requires open or interface-per-collaborator, production APIs can stay final and intention-revealing; then say where you still prefer real objects over instrumentation-heavy tests.

## The problem MockK is solving In Kotlin, classes and their members are **final unless declared `open`**. A mocking library that works by generating a *subclass proxy* — a synthetic class that extends your type and overrides each method — is stuck: you cannot override a final method, and you cannot extend a final class. Java-first mocking tools grew up on that subclass technique, which is why teams reaching for them either extracted an interface for every collaborator or sprinkled `open` across production code purely for tests. Both are production-code changes made to satisfy a test tool. ## What MockK does instead MockK does not create a subclass. It ships an **agent** (`mockk-agent`, built on ByteBuddy on the JVM; `mockk-android` uses DexMaker on Android) that performs **inline instrumentation**: it takes the already-loaded class and rewrites its bytecode so that each method body begins with a call into MockK's dispatcher. The dispatcher asks: is a recording block (`every {}` / `verify {}`) currently active on this thread? Is this instance a mock? Is there a registered answer for this call signature? Because the hook is *inside the real class's own methods*, the `final` modifier is irrelevant — there is no override happening at all. The same trick is what lets MockK later mock Kotlin `object` singletons and companion members via `mockkObject`, and JVM statics via `mockkStatic`: those are all bytecode transformations of code that already exists, not new subclasses. On a modern JVM the agent is attached dynamically at test start. Since JDK 21 the JVM prints a warning when any agent is loaded dynamically; the usual answer is the `-XX:+EnableDynamicAgentLoading` flag on the test JVM, or attaching the agent at startup. It is a warning, not a failure, but it is worth recognising in CI logs. ## How the instance is produced Instrumentation handles *dispatch*; you still need an *object*. MockK allocates the mock **without invoking a constructor** (the Objenesis technique — the JVM can allocate an instance directly, bypassing `<init>`). That means: - constructors do not run, so a class whose constructor opens a socket, reads a file, or requires 12 dependencies is still trivially mockable; - `init` blocks and property initializers do not run; - backing fields are left at JVM defaults (`null`, `0`, `false`). Uninitialised fields are harmless for a pure mock, because no real method body ever executes — the hook returns MockK's answer and the original code after it never runs. It stops being harmless the moment real code *does* run on such an instance (e.g. `callOriginal()`), which is one reason spying over an already-constructed object is a different tool. ## What this buys you, and where it stops Buys you: - final classes, final methods, `data class`es, sealed subclasses — all mockable as-is; - no `open` keyword and no interface-per-collaborator in production code just for tests; - Kotlin-specific shapes like `object` singletons and companion functions become reachable (through the dedicated `mockkObject` / `mockkStatic` entry points). Stops at anything where **there is no method dispatch to intercept**: - **inline functions** — the compiler copies the body into the caller at compile time, so at runtime there is no call to your class to hook; - **top-level and `@JvmStatic` functions, and constructors** — not instance calls; they need `mockkStatic` / `mockkConstructor`, which are separate, explicitly scoped operations; - **JDK bootstrap classes** — instrumenting core platform classes is restricted and unreliable. ## Interview framing The answer an interviewer is listening for has three beats: (1) *no subclass proxy — the agent rewrites the class's own bytecode, so `final` does not matter*; (2) *the instance is allocated without running a constructor*; (3) *the technique needs a call site to intercept, so inline/static/constructor cases fall to the dedicated MockK entry points*. Candidates who answer only "MockK just supports final classes" have memorised a feature list, not a mechanism.

  • If MockK rewrites the class's own bytecode, why can't it mock an inline function?
    An inline function's body is copied into the call site by the Kotlin compiler at compile time. At runtime the caller never issues a call to your class for that function, so there is no dispatch for MockK's hook to intercept. Writing `every { obj.someInlineFun() }` records whatever the inlined body itself does, which is usually nothing mock-related, and MockK reports a missing-call error inside the block. The fix is to make the function non-inline, or to mock a non-inline collaborator it delegates to.
  • Does the fact that no constructor runs ever cause a problem?
    For a plain mock, no: every method is intercepted before the real body could read a field, so uninitialised fields are never observed. It matters when real code does execute on the instance — for example when an answer calls the original implementation — because a property initializer that would normally populate a field never ran, and the real body can hit a null. That is a reason to spy over a properly constructed instance instead of asking a mock to run real logic.

A subclass proxy is like wrapping a machine in a new casing with your own buttons — impossible if the casing is welded shut (final). MockK instead opens the machine and solders a switch onto each internal wire, so it does not matter how the casing is built.

saying these in an interview costs you the question

  • "MockK generates a subclass, it just handles final specially" — there is no subclass; the class's own bytecode is instrumented.
  • "You still need to make Kotlin classes open for MockK" — that requirement is exactly what MockK removes.
  • "mockk() calls the constructor, so my init logic runs" — it does not; the instance is allocated without a constructor.
  • "Because it instruments bytecode, MockK can mock literally anything" — inline functions, statics/top-level functions and constructors have no instance dispatch and need different entry points.
  • Claiming the JDK 21 dynamic-agent warning is a MockK bug or a test failure — it is a JVM warning about agent attachment.

context

open as a page

When MockK builds an instance for `mockk<SomeClass>()`, are the class's constructor, `init` block and property initializers executed? Explain the consequences for the mock's internal state.

level: middleimportance: should knowfreq 28%

basics

~20 s

No. MockK allocates the instance without invoking any constructor, so init blocks and property initializers never run and backing fields stay at JVM defaults. It does not matter for a pure mock, because every method is intercepted before real code could read a field.

open as a page

MockK's `mockk()` accepts a `name` parameter, as in `mockk<Repository>(name = "primaryRepo")`. What does that name actually affect, and when is it worth setting?

level: middleimportance: should knowfreq 30%

basics

~20 s

It sets the mock's identity in its toString() and therefore in every MockK error and verification-failure message. Without it MockK prints a generated name like Repository(#1), which is ambiguous when a test holds several mocks of the same type.

open as a page

A teammate claims that because MockK instruments bytecode it can mock anything. Where does a plain `mockk<T>()` actually stop — which kinds of functions cannot be intercepted that way, and why?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Interception needs a runtime call into the instrumented class. Inline functions have none — the compiler copies their body into the caller. Top-level, @JvmStatic and constructor calls are not instance calls, so they need MockK's dedicated mockkStatic / mockkConstructor entry points, and JDK bootstrap classes are effectively off limits.

open as a page

MockK's `mockk()` takes a `moreInterfaces` parameter. What problem does it solve, and how do you stub or verify a call that belongs to one of those extra interfaces?

level: seniorimportance: nice to knowfreq 16%

basics

~20 s

It makes one mock implement additional interfaces beyond the mocked type, so code that does an is/as check — such as closing a collaborator that happens to be AutoCloseable — takes the intended branch. To stub or verify those members you cast the mock to that interface.

open as a page