MockK's `mockk()` takes a `moreInterfaces` parameter. What problem does it solve, and how do you stub or verify a call that belongs to one of those extra interfaces?
answer
- one mock, extra interfaces on the runtime type
- fixes `if (x is AutoCloseable)` branches
- vararg KClass — pass as a named argument
- cast inside every/verify to reach extra members
- smell check: runtime type test in production code
basics
~20 sIt makes one mock implement additional interfaces beyond the mocked type, so code that does an is/as check — such as closing a collaborator that happens to be AutoCloseable — takes the intended branch. To stub or verify those members you cast the mock to that interface.
solid answer
~50 sSometimes production code branches on a capability rather than the declared type: `if (source is AutoCloseable) source.close()`, or a framework that checks whether a handler also implements a lifecycle interface. A mock of only the declared type fails that check, so the branch you wanted to test is never entered. `mockk<Repository>(moreInterfaces = arrayOf(AutoCloseable::class))` produces a single mock instance that is both a `Repository` and an `AutoCloseable`. Kotlin's type system still only sees the reified type, so to touch the extra members you cast: ```kotlin verify { (repo as AutoCloseable).close() } ``` It takes a vararg of `KClass`, so pass several when the code checks several capabilities. The parameter comes after `relaxed` in the signature, so use it as a named argument. The honest caveat: needing it usually signals that production code is doing a runtime type test. If you own that code, an explicit capability in the API is often the better fix — `moreInterfaces` is the right tool when you do not.
code
kotlin · 16 linesclass Drainer {
fun drain(source: Source) {
source.readAll()
if (source is AutoCloseable) source.close()
}
}
val source = mockk<Source>(
moreInterfaces = arrayOf(AutoCloseable::class),
relaxUnitFun = true,
)
every { source.readAll() } returns "data"
Drainer().drain(source)
verify { (source as AutoCloseable).close() }go deeper
Know that it exists: it lets one mock also implement extra interfaces so runtime is checks in the code under test pass.
Be able to write it — named argument, arrayOf(X::class) — and cast inside every/verify, remembering that Unit members still need stubbing or relaxUnitFun.
Recognise the shape of code that needs it (capability checks around resources and framework SPIs) and pair it with the right relaxation option; know it is interfaces only.
Read it as a design signal: a hidden runtime type contract on a collaborator. Decide whether to make the capability explicit in the API or accept the mock option because the branching code is third-party.
## The problem Some code does not just call a collaborator — it *inspects* it: ```kotlin fun drain(source: Source) { source.readAll() if (source is AutoCloseable) source.close() // capability check } ``` This shape is common around resources (`Closeable`/`AutoCloseable`), around framework SPIs ("if the handler also implements `Ordered`, respect it"), and around legacy Java APIs that use marker interfaces. If your test creates `mockk<Source>()`, the mock implements `Source` and nothing else. The `is AutoCloseable` check is false, the branch is skipped, and the test that was supposed to prove "we close the source" silently proves nothing — or worse, `verify { ... close() }` fails and you spend twenty minutes suspecting MockK. ## What `moreInterfaces` does `moreInterfaces` is a **creation option**, a `vararg KClass<*>` in the `mockk()` signature: ```kotlin mockk<T>( name: String? = null, relaxed: Boolean = false, vararg moreInterfaces: KClass<*>, relaxUnitFun: Boolean = false, block: T.() -> Unit = {}, ): T ``` It tells MockK to build **one mock instance that additionally implements the listed interfaces**. It is one object with a wider runtime type — not a second mock, and not a wrapper. Every `is`/`as` check against those interfaces now succeeds, and calls to their members are intercepted by the same MockK dispatcher as the primary type's members. Because the parameter sits after `relaxed` in the parameter list, always pass it by name: ```kotlin val repo = mockk<Repository>( moreInterfaces = arrayOf(AutoCloseable::class), relaxUnitFun = true, ) ``` ## Stubbing and verifying the extra members The compiler still types the expression as the reified type `T` — only the *runtime* type is wider. So to reach a member of an extra interface you cast, inside the `every` / `verify` block: ```kotlin every { (repo as AutoCloseable).close() } just Runs verify { (repo as AutoCloseable).close() } ``` A readable alternative is to hold a second, typed reference to the same instance: ```kotlin val repo = mockk<Repository>(moreInterfaces = arrayOf(AutoCloseable::class)) val closeable = repo as AutoCloseable verify { closeable.close() } ``` Both refer to the same mock; MockK's recording is per instance, so calls made through either reference land in the same record. Note the practical detail in the snippet above: `close()` returns `Unit`, and on a strict mock an unstubbed call throws. Either stub it (`just Runs`) or create the mock with `relaxUnitFun = true` — which is why the two creation options are so often seen together. ## Can it be used for classes? The parameter is named `moreInterfaces` for a reason: it is for **interfaces**. The mock's primary type is the reified `T`; you cannot make one mock be two unrelated concrete classes, because a JVM object has one class. If production code checks `is SomeFinalClass`, `moreInterfaces` is not the tool — you need a mock of that class, or a design change. ## Judgement Reaching for `moreInterfaces` is a small design signal. Runtime type tests on a collaborator are a form of hidden contract: the declared parameter type does not tell you the object may also need to be closeable. If the code is yours, expressing the capability directly (a dedicated parameter, a wrapper that owns the lifecycle, or a richer interface) usually beats teaching every test about the hidden branch. `moreInterfaces` earns its place when the branching code is a framework, a third-party library, or legacy you are not refactoring today — exactly where a mock is the only way to hit the branch at all. ## Interview framing Say: *one mock instance, wider runtime type, so `is`/`as` checks in production code pass; cast inside `every`/`verify` to reach those members; pair with `relaxUnitFun` for `Unit`-returning lifecycle methods; and note that needing it often points at a runtime type test you might rather remove.*
- Can `moreInterfaces` be used to make one mock stand in for two unrelated concrete classes?No. A JVM object has exactly one class, and the mock's class is the reified type you asked for; the parameter only widens the set of implemented interfaces. If production code does `is SomeConcreteClass`, you need a mock of that class instead, or a design change that expresses the capability as an interface.
- You added `moreInterfaces = arrayOf(AutoCloseable::class)` and the test now fails with "no answer found for: … close()". What happened?The extra interface's members are ordinary mock members, so on a strict mock an unstubbed call still throws — the widened type made the branch reachable, and now the call needs an answer. Either stub it (`every { (mock as AutoCloseable).close() } just Runs`) or create the mock with `relaxUnitFun = true` so `Unit`-returning members answer automatically.
saying these in an interview costs you the question
- "It creates a second mock for the extra interface" — it is one instance with a wider runtime type.
- "You can list classes there too, to mock two class types at once" — a JVM object has one class; the parameter is for interfaces.
- "Verification needs a separate mock for the extra interface's calls" — calls through either reference land on the same instance's record.
- "You can call the extra interface's members without a cast" — the compiler still sees only the reified type.
- Treating a passing `is` check as proof the branch is tested without also verifying the call inside it.