Why is teardown (unmockkObject/unmockkStatic/unmockkConstructor/unmockkAll) critical for object, static, and constructor mocks, and how do you guarantee it?
answer
- Static-family = global JVM state, not scoped
- No cleanup → order-dependent flaky tests
- @AfterEach unmockkAll() as a safety net
- Lambda block forms auto-unmock on exit
- clear* forgets answers; unmockk* removes the patch
basics
~20 sObject, static, and constructor mocks change global JVM state shared by every test. If you don't undo them, the fake behavior leaks into later tests and causes flaky, order-dependent failures. Call unmockkAll in teardown to reset everything.
solid answer
~40 sUnlike a local `mockk<T>()` that dies with its variable, `mockkObject`, `mockkStatic`, and `mockkConstructor` patch **process-wide state**: a singleton instance, a static call site, a constructor interceptor — all shared across every test in the JVM. Without cleanup the patch persists, so a later test sees the stubbed singleton or intercepted constructor and fails unpredictably depending on execution order. Guarantee cleanup with `@AfterEach fun tearDown() = unmockkAll()` (JUnit5) or `@After` (JUnit4), which resets all MockK registrations. For surgical resets use `unmockkObject(Obj)`, `unmockkStatic(::fn)`, or `unmockkConstructor(Foo::class)`. The block forms — `mockkObject(Obj) { ... }`, `mockkStatic(...) { ... }`, `mockkConstructor(...) { ... }` — auto-unmock when the lambda exits, which is the most leak-proof pattern. There's also `clearAllMocks()` to reset recorded calls/stubs without removing the static patch; don't confuse the two: `clear*` forgets answers, `unmockk*` removes the instrumentation.
go deeper
Knows to call unmockkAll() in teardown.
Explains why static-family mocks are global and cause order-dependent failures.
Distinguishes clear* vs unmockk*, uses lambda forms, and addresses parallel-execution risk.
Institutionalises cleanup via base classes/extensions and sets policy on test isolation and parallelism for static mocking.
## The root cause: shared global instrumentation Most MockK objects are scoped: `val repo = mockk<Repo>()` lives and dies with the test method. The static-family functions are different — they mutate **global JVM state**: - `mockkObject(Obj)` replaces the **shared singleton's** behaviour. - `mockkStatic(::fn)` patches the **static dispatch** for a function the whole process uses. - `mockkConstructor(Foo::class)` installs a **constructor interceptor** affecting all future `Foo(...)`. None of this is undone automatically when the test method returns. ## The symptom: order-dependent flakiness If test A calls `mockkObject(Clock)` and stubs `now()` but never unmocks, test B that uses the real `Clock` now silently gets the frozen value — or fails — purely because A ran first. These bugs are maddening because the failing test is innocent and the result changes with test ordering, parallelism, or sharding. ## How to guarantee cleanup ```kotlin class MyTest { @AfterEach fun tearDown() = unmockkAll() // JUnit5; @After for JUnit4 } ``` `unmockkAll()` removes every object/static/constructor registration. Targeted variants: ```kotlin unmockkObject(Clock) unmockkStatic(::nowMillis) unmockkConstructor(Calculator::class) ``` ## Most leak-proof: the lambda forms Each mock-static function has a block overload that auto-unmocks on exit, even if the block throws: ```kotlin mockkObject(Clock) { every { Clock.now() } returns 0L // assertions... } // Clock automatically restored here ``` ## clear* vs unmockk* — don't confuse them - `clearMocks(...)` / `clearAllMocks()` **forget recorded calls and stubbed answers** but keep the static patch installed. - `unmockk*` **removes the instrumentation entirely**, restoring real behaviour. For static-family leakage you need `unmockk*`; merely clearing leaves the singleton/static still hijacked. ## Operational practices - Centralise `unmockkAll()` in a shared base test class or JUnit extension so nobody forgets. - Be cautious running such tests **in parallel** within one JVM — global patches and parallel methods conflict; isolate them or run serially.
- What's the difference between clearAllMocks() and unmockkAll()?clearAllMocks() forgets recorded calls and stubbed answers but keeps the static/object/constructor instrumentation installed; unmockkAll() removes the instrumentation and restores real behaviour. For leakage you need unmockkAll().
- What's the most leak-proof way to scope an object mock to a single test?Use the lambda form, e.g. mockkObject(Clock) { ... }, which auto-unmocks when the block exits even on exception.
Leaving a static mock un-torn-down is like leaving a stage prop on set after your scene — the next scene films with your fake tree in the background.
saying these in an interview costs you the question
- Saying mockkObject/Static/Constructor are auto-cleaned like local mocks
- Using clearAllMocks() expecting it to remove the static patch
- Running static-mock tests in parallel in one JVM without isolation
- No teardown, then blaming 'flaky CI' instead of leaked global state