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

Cannot mock function returning nullable value class #813

Open
TimonKanters-TomTom opened this issue Apr 14, 2022 · 5 comments
Open

Cannot mock function returning nullable value class #813

TimonKanters-TomTom opened this issue Apr 14, 2022 · 5 comments

Comments

@TimonKanters-TomTom
Copy link

TimonKanters-TomTom commented Apr 14, 2022

Expected Behavior

Being able to mock functions returning nullable value classes.

Current Behavior

It's possible to mock functions returning value classes, but only if they return a non-null value. Otherwise an exception is thrown while trying to execute the every statement.

Failure Information (for bugs)

Steps to Reproduce

Create a class with a function that returns a nullable value class (e.g., fun getInstance(): TestValueClass?) and try to mock its getInstance() call.

Note that fun getInstance(): TestValueClass (without ?) can be mocked successfully.

Context

  • MockK version: 1.12.3
  • OS: Windows 10
  • Kotlin version: 1.6.10
  • JDK version: 1.8
  • JUnit version:4.13.2
  • Type of test: unit test

Stack trace

io.mockk.MockKException: Class cast exception happened.
Probably type information was erased.
In this case use `hint` before call to specify exact return type of a method.

io.mockk.MockKException: Class cast exception happened.
Probably type information was erased.
In this case use `hint` before call to specify exact return type of a method.

	at app//io.mockk.impl.InternalPlatform.prettifyRecordingException(InternalPlatform.kt:74)
	at app//io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:66)
	at app//io.mockk.impl.eval.EveryBlockEvaluator.every(EveryBlockEvaluator.kt:30)
	at app//io.mockk.MockKDsl.internalEvery(API.kt:93)
	at app//io.mockk.MockKKt.every(MockK.kt:98)
	at app//<omitted>.ValueClassMockKTest.return nullable value class from mockk(ValueClassMockKTest.kt:98)
	at java.base@11.0.2/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@11.0.2/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base@11.0.2/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@11.0.2/java.lang.reflect.Method.invoke(Method.java:566)
	at app//org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at app//org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at app//org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at app//org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at app//org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at app//org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
	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:38)
	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
	at java.base@11.0.2/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@11.0.2/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base@11.0.2/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@11.0.2/java.lang.reflect.Method.invoke(Method.java:566)
	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 com.sun.proxy.$Proxy5.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:133)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
	at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: java.lang.ClassCastException: class java.lang.Integer cannot be cast to class <omitted>.ValueClassMockKTest$TestValueClass (java.lang.Integer is in module java.base of loader 'bootstrap'; <omitted>.ValueClassMockKTest$TestValueClass is in unnamed module of loader 'app')
	at <omitted>.ValueClassMockKTest$TestClass.getInstance-S9MxRGs(ValueClassMockKTest.kt:92)
	at <omitted>.ValueClassMockKTest$return nullable value class from mockk$mock$1$1.invoke-M5bYiII(ValueClassMockKTest.kt:98)
	at <omitted>.ValueClassMockKTest$return nullable value class from mockk$mock$1$1.invoke(ValueClassMockKTest.kt:98)
	at io.mockk.impl.eval.RecordedBlockEvaluator$record$block$1.invoke(RecordedBlockEvaluator.kt:25)
	at io.mockk.impl.eval.RecordedBlockEvaluator$enhanceWithRethrow$1.invoke(RecordedBlockEvaluator.kt:78)
	at io.mockk.impl.recording.JvmAutoHinter.autoHint(JvmAutoHinter.kt:23)
	at io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:40)
	... 47 more

Minimal reproducible code (the gist of this issue)

    @JvmInline
    value class TestValueClass(private val value: Int)

    class TestClass {
        fun getInstance(): TestValueClass? = error("Must be mocked")
    }

    @Test
    fun `return nullable value class from mockk`() {
        val mock = mockk<TestClass> {
            every { getInstance() } returns TestValueClass(1)
        }

        assertEquals(valueClassInstance, mock.getInstance())
    }

Sidenote

I realise that #152 also discussed value classes, but as part of that issue it seems main support has been added. (Thank you for that.) As such this seemed more like a separate issue and worth a dedicated bug report. Feel free to merge if this should be part of that issue instead.

@mdekaste
Copy link

Bump for visibility, especially now that duration has been released officially in 1.6, the usage of nullable value classes will grow.

@stale
Copy link

stale bot commented Aug 13, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. If you are sure that this issue is important and should not be marked as stale just ask to put an important label.

@stale stale bot added the stale label Aug 13, 2022
@TimonKanters-TomTom
Copy link
Author

This is an important issue to make effective use of value classes. @oleksiyp / @Raibaz would you be able to label this as important?

@Raibaz Raibaz added important and removed stale labels Aug 22, 2022
@NemanjaBozovic-TomTom
Copy link

Hey, any update on the issue?

@ghackett
Copy link
Contributor

Seems like this issue now applies for non-nullable value classes as of 1.13.10

interface DurationProvider {
  val myDuration: Duration
}

val mockProvider = mockk<DurationProvider> {
  every { myDuration } returns 5.minutes
}

runTest {
  delay(mockProvider.myDuration) // works fine on 1.13.9, class cast exception on 1.13.10
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants