Skip to main content

Strict exhaustiveness

Let's consider the following example:

class ClassTest {
private val a = mock<A>()
private val b = mock<B>()

@Test
fun test() {
a.call(1)
b.call(2)
// Verification passes
verify(exhaustive) {
a.call(1)
}
}
}

This behavior is expected since Mokkery can only enforce exhaustiveness checks for mocks used within the verify block.

To address this limitation, Mokkery 2.7.0 introduces MokkerySuiteScope.

warning

This approach is meaningful only when using exhaustive verification modes, such as exhaustive and exhaustiveOrder, or when using verifyNoMoreCalls.

Quick Start

To improve exhaustiveness checks in your test class, simply mark it with MokkerySuiteScope interface:

class ClassTest : MokkerySuiteScope {
private val a = mock<A>()
private val b = mock<B>()

@Test
fun test() {
a.call(1)
b.call(2)
// Verification fails - b.call(2) was not verified
verify(exhaustive) {
a.call(1)
}
}
}

Additionally, Mokkery provides the MokkerySuiteScope.verifyNoMoreCalls extension, which checks that no unverified calls remain in the scope and can help enforce exhaustiveness checks automatically:

class ClassTest : MokkerySuiteScope {
private val a = mock<A>()
private val b = mock<B>()

@Test
fun test() {
a.call(1)
b.call(2)
}

@AfterTest
fun after() {
// fails after `test` - 2 unverified calls
verifyNoMoreCalls()
}
}

How it works?

Mokkery provides overloads of mock creation functions as extensions of MokkerySuiteScope. Mocks created this way become part of the given scope.

Similarly, Mokkery provides overloads of verify and verifySuspend as extensions of MokkerySuiteScope. These overloads make verification aware of all mocks in the scope, ensuring stricter exhaustiveness checks.

class ClassTest : MokkerySuiteScope {
private val a = mock<A>() // <-- This calls `fun MokkerySuiteScope.mock(...)`, not `fun mock(...)`
private val b = mock<B>() // <-- Same as above

@Test
fun test() {
a.call(1)
b.call(2)
// Verification fails - b.call(2) was not verified
verify(exhaustive) { // <-- This calls `fun MokkerySuiteScope.verify(...)`, not `fun verify(...)`
a.call(1)
}
}
}

Manual scope creation

If you cannot mark your test class with MokkerySuiteScope, create an instance using the MokkerySuiteScope function.

with(MokkerySuiteScope()) {
val a = mock<A>()
val b = mock<B>()
a.call(1)
b.call(2)
// Verification fails
// Unverified call: b.call(2)
verify(exhaustive) {
a.call(1)
}
}