skip to content

Mocking Kotlin Constructs

Objects, companion members, top-level and extension functions, and constructors have no Java-mocking equivalent, so MockK ships dedicated machinery for each. Interviewers ask why these APIs exist to test your grasp of Kotlin's compilation model.

on this pageshow

explore

questions

13

After calling MockK's mockkConstructor(Foo::class), what does anyConstructed<Foo>() actually stand for, and which Foo instances does a stub recorded on it affect?

level: middleimportance: must knowfreq 40%

answer

  1. not an object — one shared handler per class
  2. all instances pooled, stubs and recorded calls alike
  3. unstubbed method runs the real code
  4. only instances constructed while mocked
  5. no per-instance identity

basics

~20 s

anyConstructed<Foo>() is not an instance — it is a single shared handler for the class. Every Foo constructed while the class is mocked routes its calls through it, so one stub applies to all of them. Methods you never stub fall through to the real implementation.

solid answer

~50 s

`mockkConstructor(Foo::class)` instruments the class so that instances created after that point are wired to MockK's dispatch. You never get a reference to those instances — the code under test creates them internally, which is the whole point of the feature. `anyConstructed<Foo>()` is the handle you use in `every` and `verify` to talk about them: a **single shared stub holder for the class**, not a particular object. So `every { anyConstructed<Foo>().send() } returns true` applies to the first, second and hundredth `Foo` the production code builds, and `verify { anyConstructed<Foo>().send() }` sees the calls made on all of them pooled together. Two further properties matter. Like MockK's other in-place transformations, it is **spy-like**: a method with no matching stub runs the real implementation. And the effect is bounded in time — instances constructed before `mockkConstructor` (or after the class is unmocked) behave completely normally.

code

kotlin · 7 lines
kotlin
mockkConstructor(MailClient::class)
every { anyConstructed<MailClient>().send(any(), any()) } returns true

notifier.notifyAll(users)   // constructs a MailClient per user

verify { anyConstructed<MailClient>().send(any(), any()) }
unmockkConstructor(MailClient::class)

go deeper

for a junior

Say that anyConstructed refers to the class rather than one object, and that a stub on it covers every instance the code builds.

for a middle

Add the spy-like fallthrough and the time boundary — only instances constructed while the class is mocked are affected.

for a senior

Lead with the consequences: pooled recording, no per-instance identity, no automatic isolation, and the ordering trap with pre-existing instances.

for a principal

Note that needing this at all means the code constructs its own dependencies, and treat each use as recorded coupling to a construction site.

## The problem it solves Sometimes the code under test creates its collaborator itself: ```kotlin fun notify(user: User) { val client = MailClient(config) client.send(user.email, "hi") } ``` There is no seam: you cannot inject a mock, because nothing is injected. MockK's constructor mocking gives you one by instrumenting the *class* so that the objects the production code builds are wired to MockK's dispatch. ## What anyConstructed is `mockkConstructor(MailClient::class)` installs that instrumentation. From then on, any `MailClient(...)` created by any code in the JVM is a **constructed mock**. The test never holds those objects, so MockK gives you a stand-in to record against: `anyConstructed<MailClient>()`. It is best understood as *the class's shared stub table*, not as an object. When you write ```kotlin every { anyConstructed<MailClient>().send(any(), any()) } returns true ``` you are saying: for every instance of this class created while it is mocked, a `send` call with any arguments answers `true`. The same handle is used for verification, and there the pooling is what surprises people: calls made on *different* instances are recorded together. Three instances each calling `send()` once produce three recorded `send` calls against `anyConstructed<MailClient>()`, not one each on three separate mocks. ## Spy-like fallthrough Constructor mocking is an in-place transformation, the same family as MockK's object and static mocking, and it behaves the same way: a call that matches a recorded stub is answered from the stub; a call that matches nothing runs the **real implementation**. That has two practical consequences. First, you are not automatically isolated. If the class opens a socket in a method you did not stub, that method still opens the socket. To keep a constructed collaborator inert you must stub every method the code under test will call — a stub with `any()` matchers per method is the usual shape. Second, absence of a stub is not an error. There is no strict-mock complaint pointing at the method you forgot; the test simply exercises real code, and the failure (if any) surfaces somewhere less obvious. ## When interception starts and stops The transformation is bounded in time, and reasoning about that boundary avoids most confusion: - Objects constructed **before** `mockkConstructor` ran are ordinary objects. Nothing retroactively converts them. A collaborator built in a field initializer, a lazily initialised singleton, or a setup hook that ran before the mock was installed will never be a constructed mock. - Objects constructed **while** the class is mocked route through the shared handler. - After the class is unmocked, construction is normal again. This is the first thing to check when a constructor mock "does nothing": very often the instance in question predates the call. ## The granularity limitation Because the handle is per class, you cannot say "the second instance should behave differently from the first" by identity — there is no identity to speak about. Your options are: - differentiate by **method arguments** (`every { anyConstructed<MailClient>().send(eq("a@b"), any()) } returns false`), which is often enough; - differentiate by **constructor arguments** using MockK's `constructedWith`, which filters instances by how they were built; - accept aggregate assertions and verify counts across all instances. ## Reading it back When you review a test that uses this feature, three questions cover most defects: are the instances actually created after the mock is installed; does every method the code calls have a stub, or are you happy for the real one to run; and does any assertion implicitly assume a single instance when several are constructed?

  • A collaborator is created in a field initializer of the class under test, which the test instantiates in setup before calling mockkConstructor. Is that instance a constructed mock?
    No. Only instances constructed while the class is mocked are intercepted, and this one already existed. The fix is ordering: install the constructor mock before the object under test is created, or restructure so the collaborator is created at the point of use. This ordering trap is one of the most common reasons constructor mocking appears to do nothing.
  • Does anyConstructed give you the isolation of a strict mock?
    No. Constructor mocking is spy-like: only calls matching a recorded stub are substituted, and everything else runs the real implementation. If the class performs I/O in a method you did not stub, the test performs that I/O. To be isolated you must stub every method the code under test reaches, usually with any() matchers.

saying these in an interview costs you the question

  • Believing anyConstructed<T>() returns or refers to a specific instance
  • Expecting unstubbed methods on a constructed mock to return null or throw
  • Assuming objects created before mockkConstructor are also intercepted
  • Thinking each constructed instance keeps its own separate recorded-call list
  • Reaching for constructor mocking when the collaborator could simply be injected

context

open as a page

When you call MockK's mockkObject(PricingRules) on a Kotlin object declaration, is a new instance created to stand in for the singleton, and what happens when the test then calls a method you never stubbed?

level: middleimportance: must knowfreq 40%

basics

~20 s

No new instance: MockK patches the existing singleton in place, so its identity and state are unchanged and every holder of a reference sees the patch. Unstubbed methods still run the real implementation — an object mock is a spy, not a strict mock.

open as a page

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%

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.

open as a page

MockK's mockkObject patches a singleton in place rather than substituting it. Concretely, who can observe that patch while it is active, and what does unmockkObject give back — and what does it not give back?

level: seniorimportance: must knowfreq 34%

basics

~20 s

Everything in that JVM sees it: all threads, all concurrently running tests, all reference holders, and any production path reaching the singleton. There is no per-test or per-thread scoping. unmockkObject restores real behaviour only — not the object's mutated state, nor side effects real code already performed.

open as a page

What exactly does MockK's unmockkConstructor(Foo::class) restore, and how does it differ from letting a broad unmockkAll run instead?

level: middleimportance: should knowfreq 26%

basics

~20 s

unmockkConstructor 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.

open as a page

A Kotlin class exposes a factory in its companion object, called as Report.of(row). How do you target that companion with MockK so the factory returns a canned Report, and what are the usual reasons such a stub appears to have no effect?

level: middleimportance: should knowfreq 33%

basics

~20 s

Patch the companion instance: mockkObject(Report.Companion) — writing mockkObject(Report) works too, since a bare class name with a companion resolves to that companion instance — then every { Report.of(any()) } returns fixture. Stubs miss when a different overload, a constructor call, or a top-level function is what production actually calls.

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

What does MockK's constructedWith add on top of anyConstructed, and how are its arguments written compared with the matchers you use inside an every block?

level: seniorimportance: should knowfreq 22%

basics

~20 s

constructedWith narrows a stub or verification to the constructed instances built with matching constructor arguments, instead of all of them. Its filter is written with MockK matcher objects such as EqMatcher and OfTypeMatcher, not with the every-block any()/eq() DSL.

open as a page

Code under test builds three instances of a class whose constructor you mocked with MockK, and each instance calls the same method once. What does verify(exactly = 1) { anyConstructed<T>().method() } do, and how do you assert what you meant?

level: seniorimportance: should knowfreq 30%

basics

~20 s

It fails: recorded calls are pooled per class, so MockK counts three calls, not one per instance. Assert the aggregate (exactly = 3), or split the instances by constructor arguments with constructedWith, or distinguish the calls by their own argument matchers.

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

Your team enabled parallel test execution inside a single JVM, and the tests using MockK's mockkObject started failing intermittently in ways nobody can reproduce locally. How would you reason about the cause, and what policy would you set for object mocking across the suite?

level: principalimportance: nice to knowfreq 19%

basics

~20 s

Object patching is process-global, so parallel tests share it: one stubs while another expects real behaviour, or one unmocks mid-flight. Policy: keep patched windows minimal and scoped, never unmockkAll in shared teardown under parallelism, isolate object-mocking tests from concurrent peers, and remove the need where a seam is affordable.

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