You want MockK's JUnit 5 extension to supply collaborator mocks through your test class's constructor and to build the class under test there with @InjectMockKs. What does the extension require about the order of those constructor parameters, and what happens if you get it wrong?
answer
- parameters resolved left to right, one at a time
- extension caches resolved mocks by type
- @InjectMockKs param builds real instance from cache
- MockKException: 'defined before it in test class constructor'
- same-type collaborators collide; subject last
basics
~20 sDeclare every collaborator mock before the parameter that consumes them. The extension caches resolved mocks by type as JUnit fills constructor parameters left to right, and builds the @InjectMockKs parameter from that cache; a dependency declared later is missing, and MockK throws telling you to reorder.
solid answer
~50 sJUnit resolves test-constructor parameters **one at a time, in declaration order**. MockKExtension caches each mock it creates, keyed by type. When it reaches a parameter annotated `@InjectMockKs` (or `@SpyK`), it constructs a real instance of that type by taking the type's constructor and pulling each argument from that cache — so only mocks declared **earlier** are available. ```kotlin class OrderServiceTest( @MockK val repo: OrderRepository, // first @MockK val mailer: Mailer, // second @InjectMockKs val service: OrderService, // last ) ``` Put the subject first and construction fails with a MockKException: "Unable to instantiate class … Please ensure that all dependencies needed by class are defined before it in test class constructor", listing the mocks registered so far. Two caveats: the cache is keyed by type, so two mocks of the same type collide for injection purposes, and the subject's first constructor is the one used — ambiguous multi-constructor classes are a reason to just build the subject by hand inside the test.
code
kotlin · 12 lines@ExtendWith(MockKExtension::class)
class OrderServiceTest(
@MockK val repository: OrderRepository,
@RelaxedMockK val auditLog: AuditLog,
@InjectMockKs val service: OrderService,
) {
@Test
fun `saves the order`() {
every { repository.save(any()) } returns 7L
assertEquals(7L, service.place(Order("x")))
}
}go deeper
It is enough to know MockK can fill test-constructor parameters and that the class under test must be declared after its collaborators.
Explain the left-to-right resolution and the type-keyed cache, and recognise the MockKException message as an ordering problem.
Add the limits — one cache entry per type, the subject's constructor is chosen reflectively, non-mock arguments are unsupported — and say when you would just construct the subject by hand.
Make it a convention question: pick one wiring style for the codebase, prefer explicit construction where subjects vary, and keep failure modes that only appear at class-initialisation time out of the team's way.
## Why order exists at all A JUnit 5 test class may take constructor parameters, and the framework asks each registered resolver to supply them **one parameter at a time, in declaration order**. MockKExtension exploits that: as it resolves each mock it stores it in an internal cache keyed by the parameter's type. So at any point in the constructor's parameter list, the extension knows about exactly the mocks to the left of the current position. That is fine while every parameter is an independent mock. It becomes order-sensitive the moment a parameter is not a mock but a **real object assembled from mocks** — which is what `@InjectMockKs` on a constructor parameter means, and also what `@SpyK` on a constructor parameter means, since a spy wraps a real instance. ## What the extension does for such a parameter It reflects on the parameter's type, takes a constructor of that type, and for each of that constructor's parameter types looks the value up in its cache of already-resolved mocks. Then it calls the constructor with those arguments. Two consequences fall straight out of that algorithm: 1. **A dependency resolved later is simply absent.** The lookup returns nothing for it, and construction blows up. 2. **The lookup is by type.** Two collaborators of the same type cannot be distinguished — the cache holds one entry per type, so the later one wins. Name-based disambiguation, which property-level `@InjectMockKs` can do, is not available on this path. ## The error you will actually see Get the order wrong and MockK wraps the reflective failure in a `MockKException` whose message is deliberately instructive: *"Unable to instantiate class X. Please ensure that all dependencies needed by class are defined before it in test class constructor. Already registered mocks: [...]"* — and the tail of that message lists what the cache did hold, which usually makes the missing collaborator obvious at a glance. Recognising this message and immediately thinking "parameter order" rather than "MockK is broken" is the point of the question. ## The correct shape ```kotlin @ExtendWith(MockKExtension::class) class OrderServiceTest( @MockK val repository: OrderRepository, @RelaxedMockK val auditLog: AuditLog, @InjectMockKs val service: OrderService, ) ``` Subject last, dependencies first. If the subject's own constructor takes something that is *not* a mock — a plain value like a `String` or a `Duration` — that value is not in the cache either, and this path cannot supply it; build the subject by hand instead. ## When to use this at all Constructor injection buys immutability: the mocks are `val`s, not `lateinit var`s, and the subject is fully constructed before any test runs. That reads well for a class whose subject is identical in every test. It costs flexibility. You cannot vary construction per test, you cannot stub a collaborator before the subject is built (the subject is built during resolution, though for constructor-injected collaborators that rarely matters), duplicate types are ambiguous, and multiple constructors on the subject make the choice non-obvious. The pragmatic default in most codebases stays: collaborators as `@MockK` properties, subject constructed in one line inside the test or in a `@BeforeEach`. Explicit construction is one line, immune to ordering rules, and shows the reader exactly how the subject is wired. ## Adjacent knobs, briefly Property-level `@InjectMockKs` is a different code path — it runs inside `MockKAnnotations.init` during test-instance post-processing, not through parameter resolution, and the extension can be told to resolve property injection in dependency order with `@MockKExtension.UseDependencyOrder` (or the `mockk.junit.extension.useDependencyOrder` configuration parameter). Do not confuse the two: the ordering rule described above is specific to the constructor-parameter path, and no annotation removes it, because it comes from resolving parameters left to right. ## Interview-ready summary Left-to-right resolution plus a type-keyed cache means "declare mocks before whatever consumes them". The failure is a `MockKException` telling you exactly that. And the escape hatch is always available: construct the subject explicitly and the whole ordering question disappears.
- Your class under test takes two collaborators of the same interface type. What happens with constructor-level @InjectMockKs?The extension's cache holds one entry per type, so both constructor arguments receive whichever mock was cached last for that type, and you cannot tell them apart in stubbing or verification. There is no name-based disambiguation on this path. Build the subject explicitly with the two distinct mocks instead.
- When would you skip constructor injection entirely?Whenever construction needs anything the extension cannot supply — plain values, a builder, a subject with several constructors, or per-test variation of how the subject is wired. Explicit construction inside the test is one readable line, has no ordering rule, and makes the wiring visible to the next reader. Constructor injection is worth it mainly for a class whose subject is identical across every test.
saying these in an interview costs you the question
- Assuming MockK resolves the dependency graph and order does not matter — it fills parameters strictly left to right.
- Reading the 'Unable to instantiate class' MockKException as a MockK bug rather than a parameter-ordering mistake.
- Expecting name-based matching between two same-typed collaborators on the constructor path.
- Believing @MockKExtension.UseDependencyOrder fixes constructor-parameter ordering; it concerns property injection during init.
- Putting non-mock values (strings, durations) in the subject's constructor and expecting the extension to supply them.