Skip to content
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

Using the result of callOriginal() inside coAnswers throws a fatal exception when the original contains a call to delay #1215

Open
saibotma opened this issue Feb 2, 2024 · 0 comments

Comments

@saibotma
Copy link

saibotma commented Feb 2, 2024

Expected Behavior

No exception gets thrown and the result of callOriginal() can be used.

Current Behavior

An exception gets thrown.

Failure Information (for bugs)

It works, when wrapping callOriginal() inside a suspending function:

import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

class ClassToMock() {
    suspend fun methodToMock(): Int {
        delay(1000)
        return 1
    }
}

fun main(): Unit = runBlocking {
    val mocked = mockk<ClassToMock> {
        coEvery { methodToMock() } coAnswers {
            suspend fun test(): Int = callOriginal()
            test() + 1
        }
    }

    val result = mocked.methodToMock()
    println(result)
}

It also works when removing the call to delay.

Steps to Reproduce

Run the example code.

Context

  • MockK version: 1.13.9
  • OS: MacOS Sonoma 14.1.1
  • Kotlin version: 1.9.22
  • JDK version: OpenJDK 17.0.6

Failure Logs

Stack trace

Exception in thread "main" kotlinx.coroutines.CoroutinesInternalError: Fatal exception in coroutines machinery for CancellableContinuation(DispatchedContinuation[BlockingEventLoop@3f81621c, Continuation at ClassToMock.methodToMock(main.kt:8)@7c2924d7]){Cancelled}@40fa8766. Please read KDoc to 'handleFatalException' method and report this incident to maintainers
	at kotlinx.coroutines.DispatchedTask.handleFatalException(DispatchedTask.kt:144)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:115)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280)
	at kotlinx.coroutines.EventLoopImplBase.shutdown(EventLoop.common.kt:225)
	at kotlinx.coroutines.EventLoop.decrementUseCount(EventLoop.common.kt:113)
	at kotlinx.coroutines.EventLoop.decrementUseCount$default(EventLoop.common.kt:107)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:91)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at MainKt.main(main.kt:13)
	at MainKt.main(main.kt)
	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [BlockingCoroutine{Cancelled}@7eb200ce, BlockingEventLoop@3f81621c]
Caused by: java.lang.ClassCastException: class kotlin.coroutines.jvm.internal.CompletedContinuation cannot be cast to class kotlinx.coroutines.internal.DispatchedContinuation (kotlin.coroutines.jvm.internal.CompletedContinuation and kotlinx.coroutines.internal.DispatchedContinuation are in unnamed module of loader 'app')
	at kotlinx.coroutines.CoroutineDispatcher.releaseInterceptedContinuation(CoroutineDispatcher.kt:166)
	at kotlin.coroutines.jvm.internal.ContinuationImpl.releaseIntercepted(ContinuationImpl.kt:118)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:39)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
	... 11 more
Exception in thread "main" java.lang.ClassCastException: class kotlin.coroutines.intrinsics.CoroutineSingletons cannot be cast to class java.lang.Number (kotlin.coroutines.intrinsics.CoroutineSingletons is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')
	at MainKt$main$1$mocked$1$2.invokeSuspend(main.kt:16)
	at MainKt$main$1$mocked$1$2.invoke(main.kt)
	at MainKt$main$1$mocked$1$2.invoke(main.kt)
	at io.mockk.MockKStubScope$coAnswers$1.invokeSuspend(API.kt:2259)
	at io.mockk.MockKStubScope$coAnswers$1.invoke(API.kt)
	at io.mockk.MockKStubScope$coAnswers$1.invoke(API.kt)
	at io.mockk.CoFunctionAnswer$answer$1.invokeSuspend(Answers.kt:33)
	at io.mockk.CoFunctionAnswer$answer$1.invoke(Answers.kt)
	at io.mockk.CoFunctionAnswer$answer$1.invoke(Answers.kt)
	at io.mockk.JvmCoroutineCall.callCoroutine(InternalPlatformDsl.kt:236)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at io.mockk.JvmCoroutineCall.callWithContinuation(InternalPlatformDsl.kt:241)
	at io.mockk.CoFunctionAnswer.answer(Answers.kt:34)
	at io.mockk.impl.stub.AnswerAnsweringOpportunity.answer(AnswerAnsweringOpportunity.kt:14)
	at io.mockk.impl.stub.MockKStub.answer(MockKStub.kt:54)
	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:269)
	at io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler$1.invocation(JvmMockFactoryHelper.kt:24)
	at io.mockk.proxy.jvm.advice.Interceptor.call(Interceptor.kt:21)
	at ClassToMock.methodToMock(main.kt:7)
	at MainKt$main$1.invokeSuspend(main.kt:20)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at MainKt.main(main.kt:13)
	at MainKt.main(main.kt)

Minimal reproducible code (the gist of this issue)

import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

class ClassToMock() {
    suspend fun methodToMock(): Int {
        delay(1000)
        return 1
    }
}

fun main(): Unit = runBlocking {
    val mocked = mockk<ClassToMock> {
        coEvery { methodToMock() } coAnswers {
            callOriginal() + 1
        }
    }

    val result = mocked.methodToMock()
    println(result)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant