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

every { ... } fails to called mocked implementation in particular circumstances involving sealed interface, @JvmInline value class, and multiple arguments of the same type #1264

Open
3 tasks done
tomscallon opened this issue Jun 14, 2024 · 0 comments

Comments

@tomscallon
Copy link

Prerequisites

Please answer the following questions for yourself before submitting an issue.

Expected Behavior

Calling a file-level fun that's been mocked by every (with a preceding mockkStatic(...) call) should call the mocked implementation provided by answers { ... }.

Current Behavior

Instead, the real implementation is called.

Failure Information (for bugs)

Steps to Reproduce

  1. Create a sealed interface, one of whose implementations is a @JvmInline data class
  2. Create a fun with two or more parameters of that sealed interface (they can even be nullable)
  3. Mock that fun
  4. Call that fun
  5. The original implementation will run, rather than the mocked implementation

Context

  • MockK version: 1.13.10
  • OS: macOS Sonoma
  • Kotlin version: 1.9.24
  • JDK version: 1.8 / 8
  • JUnit version: 4.13.2
  • Type of test: unit (Robolectric)

Failure Logs

Stack trace

Using test class below:

java.lang.IllegalStateException: Wasn't mocked!
at <file>Kt.fails2(<file>.kt)
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.proxy.jvm.advice.MethodCall.call(MethodCall.kt:14)
at io.mockk.proxy.jvm.advice.SelfCallEliminatorCallable.call(SelfCallEliminatorCallable.kt:14)
at io.mockk.impl.instantiation.JvmMockFactoryHelper.handleOriginalCall(JvmMockFactoryHelper.kt:96)
at io.mockk.impl.instantiation.JvmMockFactoryHelper.access$handleOriginalCall(JvmMockFactoryHelper.kt:19)
at io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler$1$invocation$1$1.invoke(JvmMockFactoryHelper.kt:28)
at io.mockk.impl.stub.MockKStub$handleInvocation$originalPlusToString$1.invoke(MockKStub.kt:233)
at io.mockk.impl.stub.SpyKStub.defaultAnswer(SpyKStub.kt:15)
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: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 <file>.fails2(<file>.kt)
at <file>Kt.fails2$default(<file>.kt)
at <file>.test(<file>.kt)
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 org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:489)
at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$2(SandboxTestRunner.java:290)
at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:104)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:840)

Minimal reproducible code (the gist of this issue)

import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@JvmInline value class Thing1(private val inner: Int)

sealed interface Thing2 {
  fun haha(): Boolean

  @JvmInline value class A(private val inner: Int) : Thing2 {
    override fun haha() = false
  }

  private data object B : Thing2 {
    override fun haha() = true
  }
}

sealed interface Thing3 {
  @JvmInline value class A(private val inner: Int) : Thing3

  @JvmInline value class B(private val inner: Int) : Thing3
}

fun works1(a: Thing1? = null, b: Thing1? = null) {
  error("Wasn't mocked!")
}
fun works2(a: Thing2? = null) {
  error("Wasn't mocked!")
}
fun works3(a: Thing3? = null) {
  error("Wasn't mocked!")
}

fun fails2(a: Thing2? = null, b: Thing2? = null) {
  error("Wasn't mocked!")
}
fun fails3(a: Thing3? = null, b: Thing3? = null) {
  error("Wasn't mocked!")
}

@RunWith(RobolectricTestRunner::class)
class SealedInterfaceValueClassTest {
  @Test
  fun test() {
    mockkStatic(::works1)

    justRun { works1(any(), any()) }
    justRun { works2(any()) }
    justRun { works3(any()) }
    justRun { fails2(any(), any()) }
    justRun { fails3(any(), any()) }

    works1()
    works2()
    works3()
    fails2()
    fails3()
  }
}
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