Mockk: Bug: capture Slots in multiple verifications capture only last value

Created on 12 Sep 2019  路  4Comments  路  Source: mockk/mockk

Expected Behavior & Current Behaviour

Capturing slots work like you'd expect them to, coming from mockito. See this test-case:

open class MockedSubject {
    open fun doSomething(id: String?, data: Any?): String {
        throw IllegalStateException("Not mocked :(")
    }
}

val mock = mockk<MockedSubject>()

every { mock.doSomething("1", "data1") } returns "result1"
every { mock.doSomething("2", "data2") } returns "result2"

mock.doSomething("1", "data1")
mock.doSomething("2", "data2")

val dataSlotId1 = slot<String>()
val dataSlotId2 = slot<String>()

verify (exactly = 1) { mock.doSomething("1", capture(dataSlotId1)) }
verify (exactly = 1) { mock.doSomething("2", capture(dataSlotId2)) }

dataSlotId1.captured shouldBe "data1"
dataSlotId2.captured shouldBe "data2"

This fails because dataSlotId1.captured is "data2"

Steps to Reproduce

I've put all code necessary to reproduce here: https://github.com/tmarsteel/mockk-verify-capture-test

Just run mvn clean test or run the test in your IDE.

Context

See the pom.xml in the repository.

  • MockK version: 1.9.3
  • OS: Ubuntu 18.04
  • Kotlin version: 1.3.41
  • JDK version: fails on OpenJDK 1.8.0_222 and 11.0.2 and HotSpot 10.0.2
  • JUnit version: fails with 4.12 and Jupiter 5.5.1
  • Type of test: unit test

Failure Logs

Screenshot from 2019-09-12 11-20-28

Stack trace

from the screenshot:

org.opentest4j.AssertionFailedError: expected: "data1" but was: "data2"
Expected :"data1"
Actual   :"data2"
<Click to see difference>


    at com.acme.ExampleTest.test(MockkDebugTest.kt:30)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:532)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:171)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:167)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:114)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:59)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:108)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
bug important

Most helpful comment

@tmarsteel Maybe not ideal, but have you tried verifyOrder?

verifyOrder {
    mock.doSomething("1", capture(dataSlotId1)) 
    mock.doSomething("2", capture(dataSlotId2))
}

All 4 comments

@tmarsteel Maybe not ideal, but have you tried verifyOrder?

verifyOrder {
    mock.doSomething("1", capture(dataSlotId1)) 
    mock.doSomething("2", capture(dataSlotId2))
}

Did you try using a list instead of a slot and then capture it?
I did it in this way and it works

val slotIds = mutableListOf<String>()
every { mock.doSomething(capture(slotIds)) } just Runs
val firstSlot = slotIds[0]
val secondSlot = slotIds[1]

@tunovic @only-fabione both work. I don't know how i solved it back then, probably with the list.

Sent out #533 to fix this.

The fix is actually suggesting what @only-fabione said earlier, to use a mutableList when capturing the same function arguments more than once.

It should be clearer now why MockK is not behaving as expected when using slots in this case.

Was this page helpful?
0 / 5 - 0 ratings