Use this skill to wire MockK (the Kotlin-first mocking framework) into a JVM unit-test suite, especially when coroutines, singleton/`object` mocking, or constructor mocking dominate. Covers `io.mockk:mockk-jvm:1.14.x` (and `mockk-android` + `mockk-agent` for instrumented), `every { } returns`, `coEvery` / `coVerify` (native suspend support — the big win over Mockito), `mockk(relaxed = true)` / `relaxUnitFun = true`, `slot<T>()` capture, `verifySequence` / `verifyOrder` / `confirmVerified`, `mockkStatic` / `mockkObject` / `mockkConstructor` with `unmockkAll()` cleanup, `@MockK` / `@RelaxedMockK` / `@SpyK` / `@InjectMockKs` annotations, and the Mockito-vs-MockK decision criteria. Use when the user mentions `coEvery`, `mockkObject`, `mockkStatic`, `slot`, `MockKException`, `every returns vs coEvery`, `relaxed = true`, or asks how to mock a Kotlin `object`/singleton.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Use this skill to wire MockK (the Kotlin-first mocking framework) into a JVM unit-test suite, especially when coroutines, singleton/`object` mocking, or constructor mocking dominate. Covers `io.mockk:mockk-jvm:1.14.x` (and `mockk-android` + `mockk-agent` for instrumented), `every { } returns`, `coEvery` / `coVerify` (native suspend support — the big win over Mockito), `mockk(relaxed = true)` / `relaxUnitFun = true`, `slot<T>()` capture, `verifySequence` / `verifyOrder` / `confirmVerified`, `mockkStatic` / `mockkObject` / `mockkConstructor` with `unmockkAll()` cleanup, `@MockK` / `@RelaxedMockK` / `@SpyK` / `@InjectMockKs` annotations, and the Mockito-vs-MockK decision criteria. Use when the user mentions `coEvery`, `mockkObject`, `mockkStatic`, `slot`, `MockKException`, `every returns vs coEvery`, `relaxed = true`, or asks how to mock a Kotlin `object`/singleton.
license
Apache-2.0. See LICENSE for complete terms.
metadata
{"author":"Jaewoong Eum (skydoves)","keywords":["android-testing","mockk","kotlin-mocking","coroutines","coEvery","coVerify","mockkStatic","mockkObject","mockkConstructor","relaxed-mock"]}
Mocking with MockK — The Kotlin-First Mocking Stack
MockK is the Kotlin-first alternative to Mockito. Its headline wins are native suspend support (coEvery / coVerify with no runBlocking dance), first-class singleton / object / static / constructor mocking, and Kotlin-native syntax (every { … } returns x). The trade-off: androidx itself does NOT use MockK (a over the AOSP checkout returns zero hits — see for the dominant pattern). New Kotlin code outside Google often picks MockK; new Kotlin code inside Google or matching AOSP conventions picks Mockito. Both are valid.
grep -r "io.mockk"
../mocking-with-mockito/SKILL.md
When to use this skill
The codebase is Kotlin-only and dominated by suspend functions / coroutines, and the user wants ergonomic suspend stubbing.
The user needs to mock a Kotlin object (singleton), companion object, top-level function (Kt-suffixed file), or every newly-constructed instance of a class.
The user wants strict-by-default verification with the option to opt out per-mock via relaxed = true / relaxUnitFun = true.
The user wants slot<T>() capture with slot.captured (cleaner than Mockito's ArgumentCaptor).
The user is writing tests for viewModelScope, Flow collectors, LaunchedEffect-style suspend collaborators.
When NOT to use this skill
The codebase is mixed Java/Kotlin or already standardized on Mockito. Use ../mocking-with-mockito/SKILL.md. Do not migrate without a reason — both frameworks are valid.
The user is matching AOSP / androidx conventions (which are Mockito-only). Use ../mocking-with-mockito/SKILL.md.
Behaviour matters more than interactions, e.g. a Repository with caching logic — write a fake instead. See ../../../fundamentals/doubles/picking-test-doubles/SKILL.md (Google explicitly prefers fakes per /test-doubles).
The runner / Gradle matrix isn't set up yet — start with ../../runner/configuring-junit4-on-android/SKILL.md.
The user is testing Flow emissions over time — pair MockK with Turbine. See ../../coroutines/testing-flows-with-turbine/SKILL.md.
The user needs runTest semantics — see ../../coroutines/testing-coroutines-with-runtest/SKILL.md.
Prerequisites
The base test wiring from ../../runner/configuring-junit4-on-android/SKILL.md is already in place.
A MainDispatcherRule is installed if any code-under-test touches Dispatchers.Main — see the runner skill.
For instrumented MockK (Android runtime), the device must be API 21+ (MockK supports back to API 21 on dexmaker).
Workflow
1. Add the MockK coordinates. Pin to a specific 1.14.x version (current latest at the time of writing):
dependencies {
// JVM unit tests (src/test/)
testImplementation("io.mockk:mockk-jvm:1.14.0")
// Instrumented tests (src/androidTest/) — different artifact + agent
androidTestImplementation("io.mockk:mockk-android:1.14.0")
androidTestImplementation("io.mockk:mockk-agent:1.14.0")
}
mockk-android swaps the bytecode-generation backend so MockK runs inside Dalvik/ART where stock ByteBuddy doesn't work. Use mockk-jvm (NOT mockk-android) for src/test/ Robolectric tests — Robolectric runs on the JVM.
2. Wire mocks via MockKAnnotations.init(this) or direct mockk<T>() construction. The annotation route reads cleaner for tests with many mocks; direct construction wins for one-off mocks:
MockKAnnotations.init does not itself install global hooks (mockkStatic / mockkObject / mockkConstructor); but if the test class adds any of those, the @After unmockkAll() is needed to prevent leakage into sibling tests. JUnit5's MockKExtension does this automatically — JUnit4 does not.
Direct construction:
val car = mockk<Car>(
name = "carA",
relaxed = false,
relaxUnitFun = true, // Unit-returning calls auto-relax; reference returns still throw
moreInterfaces = arrayOf(Comparable::class),
)
3. Stub with every { … } returns … for plain functions and coEvery { … } returns … for suspend. This is the headline ergonomic win over Mockito — no runBlocking { whenever(...) } wrap is needed for suspend stubs:
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.verify
every { car.drive(Direction.NORTH) } returns Outcome.OK
every { car.brake() } throws IllegalStateException("frozen")
every { car.gear } returnsMany listOf(1, 2, 3) // sequence
every { car.compute(any()) } answers { firstArg<Int>() * 2 }
// Suspend — the big win:
coEvery { repo.fetchUser(1) } returns User("Jane")
coEvery { repo.observeUser(1) } returns flowOf(User("Jane"))
4. Verify interactions with verify { … }, coVerify { … }, and the strict variants. MockK's verifiers are richer than Mockito's:
verify { car.drive(Direction.NORTH) }
verify(exactly = 2) { car.drive(any()) }
verify(exactly = 0) { car.brake() } // = never
verify(atLeast = 1, atMost = 3) { car.drive(any()) }
verifyAll { /* must list every interaction — exhaustive, unordered */ }
verifySequence { /* every interaction in this exact order */ }
verifyOrder { /* listed interactions occurred in this order, others may also have happened */ }
coVerify(exactly = 1) { repo.fetchUser(1) }
confirmVerified(car) // strict: nothing else was invoked
confirmVerified is the safety net to catch silent extra interactions — pair it with verifyAll/verifySequence for a fully constrained test.
5. Capture arguments with slot<T>(). Cleaner than Mockito's captor — slot.captured is a property:
import io.mockk.slot
val slot = slot<User>()
every { repo.save(capture(slot)) } returns Unit
subject.register(User("Jane"))
assertEquals("Jane", slot.captured.name)
// Multiple captures over a sequence of calls:val users = mutableListOf<User>()
every { repo.save(capture(users)) } returns Unit// ... many calls ...
assertEquals(3, users.size)
6. Use relaxed = true or relaxUnitFun = true deliberately. MockK is strict by default — any unstubbed call throws MockKException. The two relaxation modes:
relaxed = true — every unstubbed call returns a type-default value (0, false, empty string, nested mock for reference types). Cuts boilerplate but hides bugs.
relaxUnitFun = true — only Unit-returning calls auto-relax; reference returns still throw. Recommended middle ground for logger / metrics / framework-callback collaborators.
val logger = mockk<Logger>(relaxed = true) // any call -> default
logger.info("anything") // no every{} neededval n = logger.lineCount // returns 0val analytics = mockk<Analytics>(relaxUnitFun = true) // safer middle ground
analytics.track(Event("foo")) // OK — Unit return is relaxed
analytics.session // throws — reference return needs every{}
7. Mock singletons / statics / constructors with mockkObject / mockkStatic / mockkConstructor — and ALWAYS clean up. These install GLOBAL hooks that persist beyond the current test unless explicitly unmocked. Leakage across tests is the #1 MockK footgun.
import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.mockkConstructor
import io.mockk.unmockkAll
@AfterfuntearDown() = unmockkAll() // belt-and-braces cleanup@TestfunmocksObject() {
mockkObject(MySingleton)
every { MySingleton.flag } returns true// ... assertions ...
}
@TestfunmocksTopLevel() {
// Kotlin top-level functions compile as static methods on the file's *Kt class.
mockkStatic("com.example.UtilsKt")
every { hashSomething(any()) } returns "deadbeef"
}
@TestfunmocksJavaStatic() {
mockkStatic(System::class)
every { System.currentTimeMillis() } returns 0L
}
@TestfunmocksConstructor() {
mockkConstructor(OkHttpClient::class)
every { anyConstructed<OkHttpClient>().newCall(any()) } returns fakeCall
}
The JUnit5 MockKExtension auto-cleans, but JUnit4 tests must call unmockkAll() in @After.
8. Verify the test runs and the strict-mode failures actually fire../gradlew :<module>:testDebugUnitTest --tests <YourTestClass>. A passing test that uses verifyAll / verifySequence / confirmVerified on a strict mock means every interaction is accounted for. If MockK throws MockKException: no answer found for: …, the SUT is calling something the test didn't stub — either stub it, relax the mock, or fix the SUT.
Patterns
Pattern: WRONG vs RIGHT — suspend stubbing
// WRONG
every { mock.suspendFn() } returns x
// WRONG because: every { } produces a non-suspend stub. At call time MockK throws// MockKException because the actual call carries a Continuation argument that// every {} did not match against. This is the #1 trip-up when migrating from Mockito.
// RIGHT
coEvery { mock.suspendFn() } returns x
coVerify { mock.suspendFn() }
Pattern: WRONG vs RIGHT — leaking static / object mocks across tests
// WRONG@TestfunfirstTest() {
mockkStatic(System::class)
every { System.currentTimeMillis() } returns 0L// ... assertions ...// No unmockkStatic, no unmockkAll, no JUnit5 extension.
}
@TestfunsecondTest() {
// System.currentTimeMillis() is STILL mocked from firstTest. Bizarre failures// depending on test ordering follow.
}
// WRONG because: mockkStatic / mockkObject / mockkConstructor install global hooks.// JUnit4 does not auto-clean MockK state.
// RIGHT — JUnit4 with explicit teardown@AfterfuntearDown() = unmockkAll()
@TestfunfirstTest() {
mockkStatic(System::class)
every { System.currentTimeMillis() } returns 0L// ...
}
// secondTest now starts with a clean slate.
Pattern: WRONG vs RIGHT — picking a relaxation mode
// WRONGval service = mockk<PaymentService>(relaxed = true)
service.charge(amount = 100, account = "acct-7")
// Test passes because charge() silently returns a relaxed default. No verify call,// no every{} stub. The intended interaction was never asserted, and a behaviour// regression would not surface here.
relaxed = true is appropriate for loggers / metrics where the test really doesn't care about return values. For domain services, prefer the default strict mode plus explicit every / verify.
Mandatory rules
MUST use coEvery { … } returns … (NOT every { … } returns …) when stubbing a suspend function. MUST use coVerify { … } (NOT verify { … }) when verifying a suspend call.
MUST depend on io.mockk:mockk-jvm for src/test/ and io.mockk:mockk-android + io.mockk:mockk-agent for src/androidTest/. Mixing the artifacts crashes the agent.
MUST call unmockkAll() in an @After method whenever a JUnit4 test uses mockkObject, mockkStatic, or mockkConstructor. Leakage across tests is the #1 MockK footgun.
MUST prefer relaxUnitFun = true over relaxed = true when in doubt. The middle ground hides far fewer bugs.
MUST call MockKAnnotations.init(this, relaxUnitFun = …) in @Before when using @MockK / @RelaxedMockK / @SpyK / @InjectMockKs. Without it, the lateinit fields never initialise.
MUST prefer the JUnit5 MockKExtension over manual cleanup when the project is on JUnit 5 — it handles teardown of static / object mocks automatically.
MUST match the rest of the codebase's mocking choice. Do not introduce MockK into a Mockito-standardized module without team alignment, and vice versa.
MUST prefer fakes for behaviour-heavy collaborators (per developer.android.com/training/testing/fundamentals/test-doubles: "fakes ... are preferred"). Use MockK for verifying interactions, not for re-implementing behaviour.
MUST NOT mock inline functions or inline class (value classes) — MockK cannot intercept them. Wrap or refactor instead.
MUST NOT rely on mockkStatic for production-code time access (System.currentTimeMillis, Clock.system). Inject a Clock abstraction instead — production code is testable without bytecode rewriting.
PREFERRED: combine verifySequence (or verifyAll) with confirmVerified(mock) for a fully constrained test that fails if any unexpected interaction occurs.
every { mock.<suspend-fn>() } returns ... triggers MockKException("no answer found") at runtime — let your test failures, not a grep, surface this. (A grep on common suspend-fn name fragments produces too many false positives — loadConfig, fetchSync, observeForever are typically not suspending.)
Every test file using mockkStatic / mockkObject / mockkConstructor has either @After fun tearDown() = unmockkAll() or extends MockKExtension (JUnit5).
No file imports both io.mockk.* and org.mockito.* — pick one framework per module.
@MockK / @RelaxedMockK / @SpyK / @InjectMockKs fields are paired with a @Before method calling MockKAnnotations.init(this, ...).
No production code path in tests relies on mockkStatic(System::class) for time — a Clock abstraction is injected instead.
Strict-mode failures actually fire: removing a stub triggers MockKException: no answer found for: … when the SUT runs.