mockk: Bug: exception while calling mocked suspend function

In Version 1.9.3, calling a mocked suspending function throws “no answer found” even if an answer was provided.

val call = mockk<suspend () -> Int>()
coEvery { call() } returns 5
runBlocking { assertEquals(5, call()) } // throws MockKException below
  • This works if the mocked function does not suspend.
  • A workaround is to wrap the suspending function in an interface (but it would be cleaner to be able to mock the suspending function directly.)

Here’s the exception:

io.mockk.MockKException: no answer found for: Function1(#8).invoke(continuation {})
	at io.mockk.impl.stub.MockKStub.defaultAnswer(MockKStub.kt:90)
	at io.mockk.impl.stub.MockKStub.answer(MockKStub.kt:42)
	at io.mockk.impl.recording.states.AnsweringState.call(AnsweringState.kt:16)
	at io.mockk.impl.recording.CommonCallRecorder.call(CommonCallRecorder.kt:53)
	at io.mockk.impl.stub.MockKStub.handleInvocation(MockKStub.kt:263)
	at io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler$1.invocation(JvmMockFactoryHelper.kt:25)
	at io.mockk.proxy.jvm.advice.Interceptor.call(Interceptor.kt:20)
	at io.mockk.proxy.jvm.advice.BaseAdvice.handle(BaseAdvice.kt:42)
	at io.mockk.proxy.jvm.advice.jvm.JvmMockKProxyInterceptor.interceptNoSuper(JvmMockKProxyInterceptor.java:45)
	at kotlin.jvm.functions.Function1$Subclass3.invoke(Unknown Source)
	at ScopingStoreTest$put an item$1$2.invokeSuspend(ScopingStoreTest.kt:56)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.kt:116)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:79)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:53)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:35)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at MyTest$myTest$1.invokeSuspend(MyTest.kt:56)
        ...

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 25
  • Comments: 29

Commits related to this issue

Most upvoted comments

Any news on this bug ? I’m having the same bug mocking an object having a suspend function. Any workaround for this case ?

3rd birthday coming up, let’s throw a party. Who brings cake? It’s marked with Critical, Important, and Bug, better to remove those labels, looks like many users have been waiting for a fix all this time.

I think I am getting the same thing

    @Test
    fun `should listen to publish of a String`() {
        val localEventScope = LocalEventScope()

        val onReceiveMock = mockk<suspend (String) -> Unit>()
        coEvery { onReceiveMock.invoke(any<String>()) } returns Unit

        localEventScope.listen<String>(this, Dispatchers.Default, onReceiveMock)

        localEventScope.publish("test string")

        coVerify { onReceiveMock.invoke(any()) }
    }
Exception in thread "Test worker @coroutine#4" io.mockk.MockKException: no answer found for: Function2(#1).invoke(test string, continuation {})
	at io.mockk.impl.stub.MockKStub.defaultAnswer(MockKStub.kt:90)
	at io.mockk.impl.stub.MockKStub.answer(MockKStub.kt:42)
	at io.mockk.impl.recording.states.AnsweringState.call(AnsweringState.kt:16)
	at io.mockk.impl.recording.CommonCallRecorder.call(CommonCallRecorder.kt:53)
	at io.mockk.impl.stub.MockKStub.handleInvocation(MockKStub.kt:263)
	at io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler$1.invocation(JvmMockFactoryHelper.kt:25)
	at io.mockk.proxy.jvm.advice.Interceptor.call(Interceptor.kt:20)
	at io.mockk.proxy.jvm.advice.BaseAdvice.handle(BaseAdvice.kt:42)
	at io.mockk.proxy.jvm.advice.jvm.JvmMockKProxyInterceptor.interceptNoSuper(JvmMockKProxyInterceptor.java:45)
	at kotlin.jvm.functions.Function2$Subclass0.invoke(Unknown Source)
	at br.com.devsrsouza.eventkt.scopes.LocalEventScope$publish$1.invokeSuspend(LocalEventScope.kt:11)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedKt.resumeCancellable(Dispatched.kt:457)
	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26)
	at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
	at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:154)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:54)
	at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
	at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source)
	at br.com.devsrsouza.eventkt.scopes.LocalEventScope.publish(LocalEventScope.kt:9)
	at br.com.devsrsouza.eventkt.LocalEventScopeTest.should listen to publish of a String(LocalEventScopeTest.kt:19)
	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)
	....

In version 1.10.0 i am still getting the same error Exception in thread “main @coroutine#5” io.mockk.MockKException: no answer found for:

I am using Room db and calling its dao methods which are suspended

Example

interface TestDao{ suspend fun getValue():String{} }

class Test{ fun test(){ Globalscope.launch(Dispatchers.IO) { val value = db.testDao.getValue()}}

}

@Test class TestClass{ val testDao= mockk<TestDao>() coEvery { db.testDao() } returns testDao coEvery { testDao.getValue(“k1”, “s1”) } return “” }

Cannot really use this workaround @gladed is providing. I am mocking object that is injected via DI and that object’s suspend method has to return value. I cannot wrap anything within interfaces as it would mess up the production code.

interface Suspender {
    suspend fun foo(): Int
}

@Test
fun suspendMockTest() {
    val happyMock = mockk<Suspender>()
    coEvery { happyMock.foo() } returns 5
    runBlocking { assertEquals(5, happyMock.foo()) } // Pass

    val sadMock = mockk<suspend () -> Int>()
    coEvery { sadMock() } returns 5
    runBlocking { assertEquals(5, sadMock()) } // throws MockKException
}

I was injecting a suspending function through the constructor. A workaround is to declare a regular interface with an invoke function and pass a method reference to the object under test. Then the call side for the actual mocking stays identically.

So for example my class under test had a function declared in the constructor:

private val api: suspend (Key) -> Value

As a workaround instead of mocking the suspending function

private val api : suspend (Int) -> String = mockk()

I created an interface:

private interface TestApi<Key,Value> {
  suspend operator fun invoke(key: Key): Value
}

and I’m passing a method reference:

private val api: TestApi<Int,String> = mockk()
val repo = Repository(
  api = api::invoke
)

Now at the mocking side the usage stays the same:

coEvery { api(key) } returns freshValue

3rd birthday coming up, let’s throw a party. Who brings cake? It’s marked with Critical, Important, and Bug, better to remove those labels, looks like many users have been waiting for a fix all this time.

This is not how you should ask for an update on a github issue. Take notes, kids.

I’m having the same issue, any update?

I found some workaround - check if the actual last argument is a continuation, not just method types. Sad that more and more workarounds are needed, but I think anyway it has it’s value.