skip to content

Static and Extension Function Mocking

Top-level and extension functions compile to static methods on file-facade classes, which is exactly what mockkStatic targets. Interviewers ask this to check you understand the bytecode reality behind Kotlin's free functions.

on this pageshow

explore

questions

5

When you use MockK's mockkStatic to stub a Kotlin top-level or extension function, how do you work out the exact string to pass, and what can change that name?

level: middleimportance: must knowfreq 45%

answer

  1. package + FileName + Kt
  2. @file:JvmName rewrites the facade
  3. directory irrelevant, package matters
  4. string is not compiler-checked
  5. KClass overload for real classes

basics

~20 s

Pass the JVM name of the file facade class: package plus source file name with Kt appended. Greetings.kt in com.acme becomes com.acme.GreetingsKt. A @file:JvmName annotation on that file replaces the name, and the string must match exactly.

solid answer

~50 s

Kotlin has no free functions on the JVM, so the compiler puts every top-level function of a source file (including top-level extension functions) into a synthetic class called the **file facade**. Its name is `<package>.<FileName>Kt`: `Greetings.kt` in package `com.acme` compiles to `com.acme.GreetingsKt`. That is the string `mockkStatic("com.acme.GreetingsKt")` expects. The `KClass` overload is for real classes (Java statics, members) — there is no `KClass` you can write for a facade in source. Two things move the name. `@file:JvmName("Greetings")` renames the facade to `com.acme.Greetings` with no `Kt` suffix. `@file:JvmMultifileClass` merges several files into one facade under that shared name, with per-file implementation classes behind it. Directory layout is irrelevant — only the declared package and the file name matter. The string is not compiler-checked, so renaming or moving the file breaks the test at runtime rather than at build time. When unsure, read the class name out of the compiled output.

code

kotlin · 5 lines
kotlin
package com.acme

fun greet(name: String) = "hello $name"

fun String.shout() = uppercase()

go deeper

for a junior

Be able to state the rule — package plus file name plus Kt — and show one working mockkStatic call.

for a middle

Explain why the facade exists at all and name the two annotations that change it, plus how you would verify the name from the build output.

for a senior

Add the maintenance angle: the string is unchecked, so file renames break tests silently; discuss pinning the name or avoiding the seam.

for a principal

Frame it as an API-shape decision — code you intend to be mockable statically should have a deliberate, stable JVM name, or better, an injectable boundary.

## Why a string at all On the JVM every method lives in a class. Kotlin lets you declare functions at the top level of a file, so the compiler manufactures a class to hold them — the **file facade** — and the functions become static methods on it. MockK's static mocking works by instrumenting a *class*, so to intercept a top-level function you must name the facade that holds it. Since that class has no name you can write in Kotlin source (you cannot say `GreetingsKt::class`), MockK offers a `String` overload: `mockkStatic("com.acme.GreetingsKt")`. ## The naming rule The default facade name is the **declared package**, a dot, the **source file name** with the extension dropped, first letter capitalised, and the suffix `Kt`: - `com/acme/Greetings.kt`, `package com.acme` -> `com.acme.GreetingsKt` - `util/string-utils.kt` is not legal Kotlin naming for this purpose in practice; a file `StringUtils.kt` -> `...StringUtilsKt` - a file in the default package -> `GreetingsKt` with no prefix The **directory** does not participate; the `package` declaration does. A file moved between source sets or directories without changing its package keeps the same facade name. ## What changes the name `@file:JvmName("Greetings")` at the very top of the file (before the `package` line) replaces the facade name entirely: the class becomes `com.acme.Greetings` — no `Kt`. Libraries do this routinely for Java-friendly APIs, and it is the single most common reason a correct-looking `mockkStatic` string fails. `@file:JvmMultifileClass` (used together with `@file:JvmName`) merges the top-level declarations of several files into one facade with that name. The implementation is split into internal per-file classes behind the facade, which makes such functions awkward to instrument — treat a multifile facade as a signal to find a different seam rather than a naming puzzle to solve. ## The other overloads `mockkStatic(SomeJavaClass::class)` takes a `KClass` and is the right call for Java `static` methods or for members of a real class. It is the same mechanism — instrument a named class — expressed with a compiler-checked reference. Prefer it whenever a real class exists, precisely because a typo becomes a compile error instead of a runtime surprise. ## Failure mode and how to check The string is opaque to the compiler. Rename `Greetings.kt` to `GreetingText.kt` in a refactor and nothing in the IDE flags the test; it fails only when it runs. There is no partial matching and no wildcard: the name must be exactly the binary class name. To confirm a name, look at the compiled output — the `.class` files under your build directory carry the facade name directly (`build/classes/kotlin/main/com/acme/GreetingsKt.class`), or inspect the class with a bytecode viewer. Doing this once when you write the test is cheaper than debugging a silent miss later. ## Practical consequence Because the binding is by string, static mocking of top-level functions is inherently brittle against file renames. Teams that use it a lot tend to keep the mocked functions in a small, stable, deliberately named file — sometimes with an explicit `@file:JvmName` — so the facade name is a decision rather than an accident.

  • Why is there no KClass overload you can use for a top-level function?
    The facade class is synthesised by the compiler and is not addressable from Kotlin source, so there is no `SomethingKt::class` literal to write. MockK therefore accepts the binary class name as a string and resolves it reflectively. For Java statics and members of real classes the `KClass` overload exists and should be preferred, because the compiler checks it.
  • A colleague renames the file holding a mocked top-level function. What happens to the test and how would you make that safer?
    Nothing fails at compile time; the test fails when it runs, because the named facade no longer exists or no longer holds that function. Safer options are to pin the facade name with an explicit `@file:JvmName`, to keep such functions in one small stable file, or to remove the need for static mocking by routing the call through an injected interface.

saying these in an interview costs you the question

  • Passing the source file path or the package alone instead of the binary class name
  • Assuming the directory the file sits in is part of the facade name
  • Believing a typo in the string will be caught by the compiler or fail loudly at the right place
  • Thinking @file:JvmName has no effect on what mockkStatic needs
  • Trying to write GreetingsKt::class in Kotlin source

context

open as a page

A test calls MockK's mockkStatic and stubs a Kotlin top-level function, but the real implementation still runs. What are the likely causes and how do you work through them?

level: middleimportance: should knowfreq 40%

basics

~20 s

Usual causes: the function is inline so it was copied into the call site and there is nothing to intercept; the facade string is wrong because of a rename or @file:JvmName; the function is really a member or member extension, not a top-level one; the stub matched a different argument or receiver than the call used; or the call happened before the mock was installed.

open as a page

When an extension function is stubbed through MockK's mockkStatic, how does the extension's receiver take part in matching inside every/verify, and why do extensions declared inside a class or object behave differently?

level: seniorimportance: should knowfreq 36%

basics

~20 s

A top-level extension compiles to a static method whose first parameter is the receiver, so in every/verify the receiver is just argument zero: any<String>().shout() matches any receiver. An extension declared inside a class or object is an instance method of that class, so the file facade does not hold it — mock the declaring object or instance instead.

open as a page

After a test calls MockK's mockkStatic on a file facade, what happens to the functions in that facade that you never stubbed, and what exactly does unmockkStatic undo?

level: seniorimportance: should knowfreq 32%

basics

~20 s

mockkStatic patches the named class in place, spy-style: only the functions you stub with every are replaced, everything else in that class keeps running its real implementation. unmockkStatic removes the registration for that class and drops its stubs, restoring normal dispatch; other mocked classes are untouched.

open as a page

MockK's static mocking patches classes in place for the whole JVM. How would you set policy for using it across a large test suite so results stay deterministic?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Treat a patched facade as shared mutable process state: install it as late and as narrowly as possible, remove it in the same scope that installed it (block-scoped form or a guaranteed teardown), never let static-mocking tests run concurrently with code that calls the same facade, and detect leaks with randomized order plus alone-versus-suite comparison.

open as a page