What exactly does MockK's unmockkConstructor(Foo::class) restore, and how does it differ from letting a broad unmockkAll run instead?
answer
- named classes only, then construction is normal again
- mock three, unmock three
- unmockkAll = all static/object/constructor registrations
- block form removes on exit, even on throw
- removal governs future constructions, not escaped objects
basics
~20 sunmockkConstructor removes the constructor instrumentation for exactly the classes you name and drops the stubs recorded against them, so later constructions produce ordinary objects. unmockkAll removes every static, object and constructor registration in the JVM, including ones other code installed.
solid answer
~50 s`mockkConstructor(Foo::class)` installs a process-wide transformation on `Foo`. `unmockkConstructor(Foo::class)` removes it for that class: after the call, `Foo(...)` produces a plain object again and the stubs recorded against the class handle are gone. It touches nothing else — other mocked constructors, mocked objects, patched file facades and ordinary `mockk()` instances all survive. `unmockkAll()` is the blunt form: it drops **all** static, object and constructor registrations currently installed in the JVM. It is the usual safety net in a shared teardown, and its downside is exactly its strength — it also removes registrations somebody else installed deliberately. Two practical points. MockK offers a block-scoped `mockkConstructor(Foo::class) { ... }` form which removes the transformation when the block exits, including on exception, so the pairing cannot drift. And note that unmocking is about *future* constructions and dispatch — objects already created during the mocked window are not retroactively rewritten into normal objects by the removal.
code
kotlin · 14 lines// pairing
mockkConstructor(MailClient::class)
try {
every { anyConstructed<MailClient>().send(any(), any()) } returns true
notifier.notify(user)
} finally {
unmockkConstructor(MailClient::class)
}
// scoped: removed when the block exits, exception or not
mockkConstructor(MailClient::class) {
every { anyConstructed<MailClient>().send(any(), any()) } returns true
notifier.notify(user)
}go deeper
Say that unmockkConstructor removes the instrumentation for the classes you name so construction is normal again, and that unmockkAll removes everything.
Explain that removals are per class, that a test mocking several things needs all of them removed, and prefer the block-scoped form.
Bring in the failure signature — order-dependent tests from a leaked registration — and the rule that constructed mocks must not escape the test.
Set the convention: scope-bound installation plus a suite-wide net, and keep the inventory of constructor-mocked classes small and reviewed.
## What the registration is Constructor mocking is not an object you can discard; it is an instrumentation of the class installed in the running JVM. Until it is removed, **any** `Foo(...)` anywhere in the process — your test, another test, framework code — produces a constructed mock routed through MockK's dispatch, and every call on those objects that matches a stub is answered by MockK rather than by `Foo`. That framing tells you what removal has to mean and why it matters where you put it. ## unmockkConstructor: named classes only `unmockkConstructor(Foo::class)` (it accepts several classes) does two things: it removes the transformation for those classes, so construction is normal again, and it drops the stubs recorded for their class-level handles. What it deliberately does not do: - it does not remove registrations for **other** constructor-mocked classes — mock three, unmock three; - it does not touch mocked objects or patched file facades, which have their own removal calls; - it does not clear ordinary `mockk()` instances, which are unrelated garbage-collected objects. So a test that mocks a constructor and a file facade needs both removals, or the broad form. ## unmockkAll: everything at once `unmockkAll()` drops every static, object and constructor registration installed in the JVM at that moment. In a suite where nothing installs long-lived registrations on purpose, that is a sound default in a shared teardown: it makes each test independent of which specific classes its predecessors patched, and it is robust against someone adding a second `mockkConstructor` to a test and forgetting the matching removal. Its risk is symmetrical. If some setup in the suite installs a registration deliberately and expects it to persist across tests, a broad `unmockkAll` in another test's teardown silently takes it away. Note also the different axis: `unmockkAll` is about *registrations*; clearing the recorded state of ordinary mocks is a separate concern with its own API. ## Block-scoped installation The most robust shape is not a removal call at all but a scope: ```kotlin mockkConstructor(MailClient::class) { every { anyConstructed<MailClient>().send(any(), any()) } returns true notifier.notify(user) } ``` The transformation exists for the duration of the block and is removed when it exits, including on an exception thrown inside. The install and the removal cannot drift apart during editing, and a reader sees the exact extent of the global change on screen. Use it whenever the constructing call is localised; fall back to setup/teardown when several test methods share the arrangement. ## Ordering and what removal cannot undo Removal affects the future. Instances constructed while the class was mocked are already wired the way they were wired; taking the registration away governs dispatch and later constructions, not a retroactive rewrite of objects you already handed to something else. In practice this matters when a test stashes a constructed object in a longer-lived structure — a cache, a static field, a fixture reused by the next test. Do not let constructed mocks escape the test that made them. ## Consequences of getting it wrong A constructor registration left installed changes object creation for every test that follows in that JVM, silently, because unmatched calls fall through to the real implementation and nothing complains. The observable symptom is order dependence: a test passes alone and fails, or passes for the wrong reason, when the suite runs. If tests execute concurrently in one JVM, the interference is not even deterministic. That is why the healthy pattern is scope-bound installation plus a broad teardown net, rather than a hand-maintained list of removals.
- A test mocks two constructors and one file facade. What must its teardown do?Either remove all three registrations explicitly — unmockkConstructor for both classes and the static removal for the facade — or call the broad unmockkAll, which drops every static, object and constructor registration at once. The failure mode of the explicit route is forgetting one when the test is edited later, which is why many suites keep the broad form as a standing net regardless of what individual tests do.
- Does removing a constructor mock turn objects already constructed during the mocked window back into normal objects?Treat them as unusable rather than restored: removal governs dispatch and future constructions, and objects created during the mocked window were wired while the transformation was installed. The practical rule is that constructed mocks must not escape the test that created them — never stash one in a cache, a static field or a fixture that outlives the test.
saying these in an interview costs you the question
- Assuming unmockkConstructor on one class cleans up other constructor, object or static registrations
- Confusing removing a registration with clearing the recorded state of ordinary mocks
- Putting the removal at the end of the test body, where a failure skips it
- Believing a leftover registration will announce itself with an error rather than silently changing later tests
- Letting a constructed mock be stored somewhere that outlives the test