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

ClassCastException when calling a mocked generic function #1098

Open
3 tasks done
ghackett opened this issue Jun 13, 2023 · 0 comments
Open
3 tasks done

ClassCastException when calling a mocked generic function #1098

ghackett opened this issue Jun 13, 2023 · 0 comments

Comments

@ghackett
Copy link
Contributor

ghackett commented Jun 13, 2023

Prerequisites

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

  • I am running the latest version
  • I checked the documentation and found no answer
  • I checked to make sure that this issue has not already been filed

Expected Behavior

Given an class/interface with a generic function where the return type is based on the input type, i.e.

interface InterfaceToMock {
    fun <OP> doThing(op: OP): OP
}

Stubbing this method multiple times with different types of any<T>() should result in different return values for each call. i.e.

        every { thing.doThing(any<Impl1>()) } returns Impl1()
        every { thing.doThing(any<Impl2>()) } returns Impl2()

        val result1 = thing.doThing(Impl1()) // result1 should be an instance of Impl1()
        val result2 = thing.doThing(Impl2()) // result2 should be an instance of Impl2()

Current Behavior

java.lang.ClassCastException: class Impl2 cannot be cast to class Impl1 (Impl2 and Impl1 are in unnamed module of loader 'app')

However there are two interesting workarounds...

  1. if you remove val result1 = and val result2 = from the above statement, there is no crash.
  2. if you replace any<Impl1>() and any<Impl2>() with capture(slot<Impl1>()) and capture(slot<Impl2>()) respectively, the test works as expected

Full sample test

Failure Information (for bugs)

Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template.

Context

Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.

  • MockK version: 1.13.3 and 1.13.4
  • OS: OSX ventura 13.4 & Ubunutu 20.04
  • Kotlin version: 1.8.10
  • JDK version: 17
  • JUnit version: 4.13
  • Type of test: unit test

Failure Logs

Please include any relevant log snippets or files here.

Stack trace

// -----------------------[ YOUR STACK STARTS HERE ] -----------------------
class Impl2 cannot be cast to class Impl1 (Impl2 and Impl1 are in unnamed module of loader 'app')
java.lang.ClassCastException: class Impl2 cannot be cast to class Impl1 (Impl2 and Impl1 are in unnamed module of loader 'app')
	at SampleTest.sampleTest_fails(SampleTest.kt:21)
	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.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:108)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:40)
	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52)
	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.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at jdk.proxy1/jdk.proxy1.$Proxy2.processTestClass(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)

// -----------------------[ YOUR STACK TRACE ENDS HERE ] -----------------------

Minimal reproducible code (the gist of this issue)

https://github.com/ghackett/test-mockk-project/blob/main/src/test/kotlin/SampleTest.kt#L17

// -----------------------[ GRADLE DEFINITIONS ] -----------------------
dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.13'
}
// -----------------------[ YOUR CODE STARTS HERE ] -----------------------
package io.mockk.gh

import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import org.junit.Test

class Impl1
class Impl2

interface InterfaceToMock {
    fun <OP> doThing(op: OP): OP
}

class SampleTest {
    private val thing: InterfaceToMock = mockk()

    @Test fun sampleTest_fails() {
        every { thing.doThing(any<Impl1>()) } returns Impl1()
        every { thing.doThing(any<Impl2>()) } returns Impl2()

        val result1 = thing.doThing(Impl1())
        val result2 = thing.doThing(Impl2())
    }
}
// -----------------------[ YOUR CODE ENDS HERE ] -----------------------
@ghackett ghackett changed the title ClassCastException when mocking a generic function ClassCastException when calling a mocked generic function Jun 13, 2023
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