How does mockkStatic let you stub Kotlin top-level functions, extension functions, and Java static methods, and what do you pass to it?
answer
- Top-level/extension fns → static on FileKt class
- Pass class, "…Kt" string, or function reference
- Function reference is refactor-safe
- Extensions dispatch statically by receiver type
- unmockkStatic / unmockkAll teardown
basics
~10 smockkStatic tells MockK to intercept functions that have no instance — top-level functions, extension functions, and Java statics. You register the file/class, then stub with every { } and undo with unmockkStatic or unmockkAll.
solid answer
~40 sTop-level Kotlin functions and extensions compile to **static methods on a synthetic `…Kt` class**, so they have no object you can `mockk`. `mockkStatic` patches the bytecode dispatch for those static targets. There are several overloads: pass a `KClass`/`Class` for Java statics (`mockkStatic(Collections::class)`); pass a fully-qualified **JVM class name string** for the generated file class (`mockkStatic("com.app.UtilsKt")`); or, on newer MockK, pass a **function reference** directly (`mockkStatic(::myTopLevelFn)` or `mockkStatic(String::myExtension)`), which is the safest because it survives refactors. After registering, stub with `every { myTopLevelFn(any()) } returns ...` and verify normally. Tear down with `unmockkStatic(...)` or `unmockkAll()`. A classic use is mocking `android.util.Log` or a top-level `currentTimeMillis()` wrapper. Note extension functions are resolved statically by receiver type, so you mock the declared type, not a subtype.
code
kotlin · 5 linesmockkStatic(UUID::class)
every { UUID.randomUUID() } returns UUID.fromString("00000000-0000-0000-0000-000000000000")
assertEquals("00000000-0000-0000-0000-000000000000", UUID.randomUUID().toString())
verify { UUID.randomUUID() }
unmockkStatic(UUID::class)go deeper
Knows mockkStatic exists and that it targets static/top-level functions with every {}.
Explains the FileKt compilation, the three overloads, and prefers the function-reference form.
Understands static-dispatch nuances for extensions and global teardown ordering.
Argues for dependency injection over static mocking and weighs maintainability/coupling trade-offs.
## Why static mocking is a separate API MockK substitutes behaviour by controlling how a call is **dispatched**. For a normal object that means proxying the instance. But Kotlin **top-level functions** and **extension functions** are compiled to `static` methods on a synthetic class named after the file: a function in `Utils.kt` lives on `UtilsKt`. **Java static methods** (e.g. `Collections.emptyList()`) are likewise instance-less. There is no object to proxy, so `mockkStatic` instruments the static call site itself. ## The overloads you can pass - **By Java/Kotlin class for statics:** `mockkStatic(Collections::class)` then `every { Collections.emptyList<Any>() } returns listOf(1)`. - **By JVM class-name string** for top-level/extension functions: `mockkStatic("com.app.UtilsKt")`. You must know the generated `…Kt` name (changeable via `@file:JvmName`). - **By function reference (preferred, modern MockK):** `mockkStatic(::computeHash)` for a top-level function, or `mockkStatic(String::sanitize)` for an extension on `String`. Refactor-safe and avoids guessing the `Kt` class name. ```kotlin // Utils.kt fun nowMillis(): Long = System.currentTimeMillis() fun String.slug(): String = lowercase().replace(' ', '-') @AfterEach fun tearDown() = unmockkAll() @Test fun stubsTopLevelAndExtension() { mockkStatic(::nowMillis) every { nowMillis() } returns 0L assertEquals(0L, nowMillis()) mockkStatic(String::slug) every { "Hello World".slug() } returns "x" assertEquals("x", "Hello World".slug()) } ``` ## Extension-function gotcha An extension is dispatched **statically by its declared receiver type**, not virtually. If you mock `String::slug`, only calls resolved as `String.slug()` are intercepted — a call seen by the compiler as `CharSequence.slug()` (a different declaration) is not. ## Java statics example ```kotlin mockkStatic(UUID::class) every { UUID.randomUUID() } returns UUID.fromString("00000000-0000-0000-0000-000000000000") ``` ## Teardown Static mocks are global too. Use `unmockkStatic(::nowMillis)` / `unmockkStatic(UUID::class)` for the specific target, or `unmockkAll()` to reset everything. The `mockkStatic(...) { }` lambda form auto-unmocks at block end. ## When NOT to reach for it Mocking statics often signals hidden coupling. Prefer injecting a small interface (a `Clock`, an `IdGenerator`) so production code is testable without bytecode tricks; reserve `mockkStatic` for third-party/Android statics you cannot change.
- What class hosts a Kotlin top-level function at the JVM level, and how does that affect mockkStatic?It is a synthetic class named after the file plus 'Kt' (e.g. Utils.kt -> UtilsKt), overridable with @file:JvmName. With the string overload you must use that exact name; the function-reference overload avoids needing to know it.
- Why might mockkStatic on an extension function not intercept a call you expected?Extensions are dispatched statically by declared receiver type. If the call is resolved against a different declaration/receiver type than the one you mocked, MockK won't intercept it.
saying these in an interview costs you the question
- Thinking mockkStatic works on instance methods
- Assuming extension functions dispatch virtually like overrides
- Hardcoding the wrong …Kt class name and not knowing @file:JvmName changes it
- Never tearing down, leaking the static mock across tests