New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix verifier logic for slots and different matchers #951
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,15 +55,17 @@ 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." | ||
throw MockKException(msg) | ||
} | ||
|
||
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
matcher.captureAnswer(call) | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Service> { | ||
every { method(any()) } just Runs | ||
} | ||
|
||
service.method(Subclass1()) | ||
service.method(Subclass2()) | ||
|
||
val slot = slot<Subclass2>() | ||
verify(exactly = 1) { service.method(capture(slot)) } | ||
assertTrue(slot.isCaptured) | ||
assertIs<Subclass2>(slot.captured) | ||
} | ||
|
||
@Test | ||
fun `test ordered`() { | ||
val service = mockk<Service> { | ||
every { method(any()) } just Runs | ||
} | ||
|
||
service.method(Subclass1()) | ||
service.method(Subclass2()) | ||
|
||
val slot = slot<Subclass2>() | ||
verifyOrder { | ||
service.method(any()) | ||
service.method(capture(slot)) | ||
} | ||
assertTrue(slot.isCaptured) | ||
assertIs<Subclass2>(slot.captured) | ||
} | ||
|
||
@Test | ||
fun `test sequence`() { | ||
val service = mockk<Service> { | ||
every { method(any()) } just Runs | ||
} | ||
|
||
service.method(Subclass1()) | ||
service.method(Subclass2()) | ||
|
||
val slot = slot<Subclass2>() | ||
verifySequence { | ||
service.method(any()) | ||
service.method(capture(slot)) | ||
} | ||
assertTrue(slot.isCaptured) | ||
assertIs<Subclass2>(slot.captured) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -85,8 +85,8 @@ class CapturingTest { | |
|
||
assertFailsWith<MockKException> { | ||
verify { | ||
mock.doSomething("1", capture(dataSlotId1)) | ||
mock.doSomething("2", capture(dataSlotId2)) | ||
Comment on lines
-88
to
-89
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would not fail if left as is, because with different values of the first argument only 1 call will be matched in each case |
||
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) | ||
Comment on lines
+139
to
+140
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously this was 4 |
||
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<String>() | ||
val dataSlotId2 = slot<String>() | ||
|
||
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) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's OK if there are multiple calls overall but only one of them matches and will be captured