diff --git a/modules/mockk-agent/src/jvmMain/kotlin/io/mockk/proxy/jvm/ObjenesisInstantiator.kt b/modules/mockk-agent/src/jvmMain/kotlin/io/mockk/proxy/jvm/ObjenesisInstantiator.kt index ce809c546..0f9844776 100644 --- a/modules/mockk-agent/src/jvmMain/kotlin/io/mockk/proxy/jvm/ObjenesisInstantiator.kt +++ b/modules/mockk-agent/src/jvmMain/kotlin/io/mockk/proxy/jvm/ObjenesisInstantiator.kt @@ -24,6 +24,10 @@ class ObjenesisInstantiator( if (cls == Any::class.java) { @Suppress("UNCHECKED_CAST") return Any() as T + } else if (cls.isSealedSafe()) { + cls.getPermittedSubclassesSafe().firstNotNullOfOrNull { subCls -> + runCatching { instance(subCls) }.getOrNull() + } ?: error("could not find subclass for sealed class $cls") } else if (!Modifier.isFinal(cls.modifiers)) { try { val instance = instantiateViaProxy(cls) @@ -42,6 +46,27 @@ class ObjenesisInstantiator( return instanceViaObjenesis(cls) } + /** + * `boolean Class.isSealed()` is only available with JDK 17+. Used via reflection to support + * builds with previous Java versions as well. This should be refactored to use the actual + * method once the minimum Java version is 17. + */ + private fun Class<*>.isSealedSafe(): Boolean = + javaClass.methods.firstOrNull { it.name == "isSealed" }?.invoke(this) == true + + /** + * `Class[] Class.getPermittedSubclasses` is only available with JDK 17+. Used via reflection + * to support builds with previous Java versions as well. This should be refactored to use the + * actual method once the minimum Java version is 17. + */ + private fun Class<*>.getPermittedSubclassesSafe(): Array> = + javaClass.methods.firstOrNull { it.name == "getPermittedSubclasses" } + ?.let { + @Suppress("UNCHECKED_CAST") + it.invoke(this) as Array> + } + ?: emptyArray() + private fun instantiateViaProxy(cls: Class): T? { val proxyCls = if (!Modifier.isAbstract(cls.modifiers)) { log.trace("Skipping instantiation subsclassing $cls because class is not abstract.") diff --git a/modules/mockk/src/commonTest/kotlin/io/mockk/it/SealedClassTest.kt b/modules/mockk/src/commonTest/kotlin/io/mockk/it/SealedClassTest.kt index f48c8b1f4..efa39cab2 100644 --- a/modules/mockk/src/commonTest/kotlin/io/mockk/it/SealedClassTest.kt +++ b/modules/mockk/src/commonTest/kotlin/io/mockk/it/SealedClassTest.kt @@ -2,7 +2,6 @@ package io.mockk.it import io.mockk.every import io.mockk.mockk -import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals @@ -10,7 +9,6 @@ import kotlin.test.assertEquals class SealedClassTest { @Test - @Ignore("Fails on JDK17+ https://github.com/mockk/mockk/issues/832") fun serviceReturnsSealedClassImpl() { val factory = mockk { every { create() } returns Leaf(1) @@ -22,7 +20,6 @@ class SealedClassTest { } @Test - @Ignore("Fails on JDK17+ https://github.com/mockk/mockk/issues/832") fun serviceAnswersSealedClassImpl() { val factory = mockk { every { create() } answers { Leaf(1) } diff --git a/modules/mockk/src/commonTest/kotlin/io/mockk/it/SealedInterfaceTest.kt b/modules/mockk/src/commonTest/kotlin/io/mockk/it/SealedInterfaceTest.kt index 4abe70221..91f727abf 100644 --- a/modules/mockk/src/commonTest/kotlin/io/mockk/it/SealedInterfaceTest.kt +++ b/modules/mockk/src/commonTest/kotlin/io/mockk/it/SealedInterfaceTest.kt @@ -3,14 +3,12 @@ package io.mockk.it import io.mockk.every import io.mockk.mockk import kotlin.test.Test -import kotlin.test.Ignore import kotlin.test.assertEquals class SealedInterfaceTest { @Test - @Ignore("Fails on JDK17+ https://github.com/mockk/mockk/issues/832") fun serviceReturnsSealedClassImpl() { val factory = mockk { every { create() } returns Leaf(1) @@ -22,7 +20,6 @@ class SealedInterfaceTest { } @Test - @Ignore("Fails on JDK17+ https://github.com/mockk/mockk/issues/832") fun serviceAnswersSealedClassImpl() { val factory = mockk { every { create() } answers { Leaf(1) }