diff --git a/modules/mockk/src/commonMain/kotlin/io/mockk/impl/verify/UnorderedCallVerifier.kt b/modules/mockk/src/commonMain/kotlin/io/mockk/impl/verify/UnorderedCallVerifier.kt index dcb5d8ebc..79c164960 100644 --- a/modules/mockk/src/commonMain/kotlin/io/mockk/impl/verify/UnorderedCallVerifier.kt +++ b/modules/mockk/src/commonMain/kotlin/io/mockk/impl/verify/UnorderedCallVerifier.kt @@ -55,7 +55,9 @@ open class UnorderedCallVerifier( val allCallsForMock = stub.allRecordedCalls() val allCallsForMockMethod = stub.allRecordedCalls(matcher.method) - if(allCallsForMockMethod.size > 1 && matcher.args.any { it is CapturingSlotMatcher<*> }) { + val matchedCalls = allCallsForMockMethod.filter(matcher::match) + + if(matchedCalls.size > 1 && matcher.args.any { it is CapturingSlotMatcher<*> }) { val msg = "$matcher execution is being verified more than once and its arguments are being captured with a slot.\n" + "This will store only the argument of the last invocation in the slot.\n" + "If you want to store all the arguments, use a mutableList to capture arguments." @@ -63,7 +65,7 @@ open class UnorderedCallVerifier( } val result = if (min == 0 && max == 0) { - if (!allCallsForMockMethod.any(matcher::match)) { + if (matchedCalls.isEmpty()) { VerificationResult.OK(listOf()) } else { VerificationResult.Failure( @@ -127,7 +129,6 @@ open class UnorderedCallVerifier( } } else -> { - val matchedCalls = allCallsForMockMethod.filter(matcher::match) val n = matchedCalls.count() if (n in min..max) { VerificationResult.OK(matchedCalls) @@ -162,7 +163,7 @@ open class UnorderedCallVerifier( } captureBlocks.add { - for (call in allCallsForMockMethod) { + for (call in matchedCalls) { matcher.captureAnswer(call) } } diff --git a/modules/mockk/src/commonTest/kotlin/io/mockk/it/CaptureSubclassVerificationTest.kt b/modules/mockk/src/commonTest/kotlin/io/mockk/it/CaptureSubclassVerificationTest.kt new file mode 100644 index 000000000..714de0e6b --- /dev/null +++ b/modules/mockk/src/commonTest/kotlin/io/mockk/it/CaptureSubclassVerificationTest.kt @@ -0,0 +1,80 @@ +package io.mockk.it + +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify +import io.mockk.verifyOrder +import io.mockk.verifySequence +import kotlin.test.Test +import kotlin.test.assertIs +import kotlin.test.assertTrue + +/** + * See issue #774. + */ +class CaptureSubclassVerificationTest { + + interface Interface + + class Subclass1 : Interface + + class Subclass2 : Interface + + interface Service { + fun method(obj: Interface) + } + + @Test + fun `test unordered`() { + val service = mockk { + every { method(any()) } just Runs + } + + service.method(Subclass1()) + service.method(Subclass2()) + + val slot = slot() + verify(exactly = 1) { service.method(capture(slot)) } + assertTrue(slot.isCaptured) + assertIs(slot.captured) + } + + @Test + fun `test ordered`() { + val service = mockk { + every { method(any()) } just Runs + } + + service.method(Subclass1()) + service.method(Subclass2()) + + val slot = slot() + verifyOrder { + service.method(any()) + service.method(capture(slot)) + } + assertTrue(slot.isCaptured) + assertIs(slot.captured) + } + + @Test + fun `test sequence`() { + val service = mockk { + every { method(any()) } just Runs + } + + service.method(Subclass1()) + service.method(Subclass2()) + + val slot = slot() + verifySequence { + service.method(any()) + service.method(capture(slot)) + } + assertTrue(slot.isCaptured) + assertIs(slot.captured) + } +} diff --git a/modules/mockk/src/commonTest/kotlin/io/mockk/it/CapturingTest.kt b/modules/mockk/src/commonTest/kotlin/io/mockk/it/CapturingTest.kt index dd9f310cb..57f5c56b7 100644 --- a/modules/mockk/src/commonTest/kotlin/io/mockk/it/CapturingTest.kt +++ b/modules/mockk/src/commonTest/kotlin/io/mockk/it/CapturingTest.kt @@ -85,8 +85,8 @@ class CapturingTest { assertFailsWith { verify { - mock.doSomething("1", capture(dataSlotId1)) - mock.doSomething("2", capture(dataSlotId2)) + mock.doSomething(any(), capture(dataSlotId1)) + mock.doSomething(any(), capture(dataSlotId2)) } } } @@ -136,6 +136,8 @@ class CapturingTest { mock.doSomething("2", capture(slotList)) } + // Each capture should have happened once because of different matchers for `id` argument + assertEquals(slotList.size, 2) assertEquals("data1", slotList[0]) assertEquals("data2", slotList[1]) } @@ -158,6 +160,23 @@ class CapturingTest { assertEquals(args, list) } + @Test + fun itDoesNotThrowAMockkExceptionWhenVerifyingTheSameFunctionTwiceWithSlotsWithDifferentMatchers() { + mock.doSomething("1", "data1") + mock.doSomething("2", "data2") + + val dataSlotId1 = slot() + val dataSlotId2 = slot() + + verify { + mock.doSomething("1", capture(dataSlotId1)) + mock.doSomething("2", capture(dataSlotId2)) + } + + assertEquals("data1", dataSlotId1.captured) + assertEquals("data2", dataSlotId2.captured) + } + class MockNullableCls { fun call(unused: String?) { println(unused)