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

MockKException: Can't instantiate proxy for class Sealed on 1.13.2 #945

Closed
SimonMarquis opened this issue Oct 7, 2022 · 4 comments
Closed

Comments

@SimonMarquis
Copy link
Contributor

Hi,

The version 1.13.2 introduced a regression, probably because of #916

Here is a code snippet to reproduce the issue:

sealed class Sealed
sealed class SubSealed : Sealed()
object Z : Sealed()

class Test {
    @Test
    fun test() {
        val instantiate: () -> Sealed = mockk()
        every { instantiate() } returns Z
    }
}
Click to view stacktrace...

Can't instantiate proxy for class Sealed
io.mockk.MockKException: Can't instantiate proxy for class Sealed
	at app//io.mockk.impl.instantiation.JvmMockFactory.newProxy(JvmMockFactory.kt:64)
	at app//io.mockk.impl.instantiation.AbstractMockFactory.newProxy$default(AbstractMockFactory.kt:24)
	at app//io.mockk.impl.instantiation.AbstractMockFactory.mockk(AbstractMockFactory.kt:59)
	at app//io.mockk.impl.stub.MockKStub$childMockK$1$1$1.invoke(MockKStub.kt:201)
	at app//io.mockk.impl.stub.MockKStub$childMockK$1$1$1.invoke(MockKStub.kt:200)
	at app//io.mockk.impl.InternalPlatform.customComputeIfAbsent(InternalPlatform.kt:43)
	at app//io.mockk.impl.stub.MockKStub$childMockK$1$1.invoke(MockKStub.kt:200)
	at app//io.mockk.impl.recording.CommonCallRecorder.safeExec(CommonCallRecorder.kt:72)
	at app//io.mockk.impl.log.SafeToString.exec(SafeToString.kt:10)
	at app//io.mockk.impl.stub.MockKStub.childMockK(MockKStub.kt:199)
	at app//io.mockk.impl.recording.PermanentMocker.permamentize(PermanentMocker.kt:48)
	at app//io.mockk.impl.recording.PermanentMocker.mock(PermanentMocker.kt:24)
	at app//io.mockk.impl.recording.states.RecordingState.mockPermanently(RecordingState.kt:123)
	at app//io.mockk.impl.recording.states.RecordingState.round(RecordingState.kt:33)
	at app//io.mockk.impl.recording.CommonCallRecorder.round(CommonCallRecorder.kt:50)
	at app//io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:63)
	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//Test.test(Test.kt:15)
	at java.base@11.0.16.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@11.0.16.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base@11.0.16.1/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@11.0.16.1/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.16.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@11.0.16.1/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base@11.0.16.1/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@11.0.16.1/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.$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: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: io.mockk.proxy.MockKAgentException: Failed to subclass class Sealed
	at app//io.mockk.proxy.jvm.ProxyMaker.proxy(ProxyMaker.kt:38)
	at app//io.mockk.impl.instantiation.JvmMockFactory.newProxy(JvmMockFactory.kt:34)
	... 62 more
Caused by: java.lang.IllegalStateException: Unable to create proxy for sealed class class SubSealed, available subclasses: []
	at io.mockk.proxy.jvm.ProxyMaker.subclass(ProxyMaker.kt:113)
	at io.mockk.proxy.jvm.ProxyMaker.subclass(ProxyMaker.kt:112)
	at io.mockk.proxy.jvm.ProxyMaker.proxy(ProxyMaker.kt:35)
	... 63 more

To reproduce the bug, the name of sealed classes have their importance.
The current algorithm of ProxyMaker.subclass will first check sealedSubclasses of the Sealed class, which contains SubSealed and Z (in this order).
Then, it will recurse with SubSealed but throws an error because it has no sealedSubclasses.

@SimonMarquis
Copy link
Contributor Author

@SimonMarquis
Copy link
Contributor Author

I can create a failing test in the codebase if you want to.

@SimonMarquis
Copy link
Contributor Author

Wrapping the subclass() method call inside the firstNotNullOfOrNull loop seems to make the test succeed again:

clazz.kotlin.sealedSubclasses.firstNotNullOfOrNull {
    @Suppress("UNCHECKED_CAST")
    kotlin.runCatching { subclass(it.java, interfaces) as Class<T> }.getOrNull()
} ?: error("Unable to create proxy for sealed class $clazz, available subclasses: ${clazz.kotlin.sealedSubclasses}")

@SimonMarquis
Copy link
Contributor Author

I'm not able to reproduce it anymore.

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