mockk: java.lang.InstantiationError when upgrading to Kotlin 1.7
Java 17 + Kotlin 1.7 + Mockk 1.12.4/Spring Mockk 3.1.1
java.lang.InstantiationError: com.example.demo.UpdateAccountResult
at jdk.internal.reflect.GeneratedSerializationConstructorAccessor18.newInstance(Unknown Source)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
It occurred whens using coEvery{}
to do some stub, which returns a UpdateAccountResult.Success()
.
Here UpdateAccountResult
is a sealed class
, it has some detailed result subclass/sub data classes.
And UpdateAccountResult
extends from a base abstract class like this.
abstract class ApiBaseResult {
@field:JsonProperty("SC")
val statusCode: String? = null
@field:JsonProperty("EC")
val errorCode: String? = null
@field:JsonProperty("EM")
val errorMessage: String? = null
}
These codes are working well with Kotlin 1.6.21.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 60
- Comments: 57 (13 by maintainers)
Links to this issue
Commits related to this issue
- Sealed class funker ikke med kotling 1.7 og Mockk https://github.com/mockk/mockk/issues/832 — committed to navikt/dp-mellomlagring by gtcno 2 years ago
- E nedgraderer kotlin pga mockk-issue https://github.com/mockk/mockk/issues/832 - som igjen feiler tester i prefill. — committed to navikt/ep-personoppslag by kjetiljd 2 years ago
- E oppgraderer mockito-kotlin -> '1.7.10' // 1.7.0 skaper trøbbel for mockk: https://github.com/mockk/mockk/issues/832 - som igjen feiler tester i prefill-appen — committed to navikt/ep-personoppslag by dskarpas 2 years ago
- Revert "E oppgraderer mockito-kotlin -> '1.7.10' // 1.7.0 skaper trøbbel for mockk: https://github.com/mockk/mockk/issues/832 - som igjen feiler tester i prefill-appen" This reverts commit 63c536cb3a... — committed to navikt/ep-personoppslag by dskarpas 2 years ago
- Revert "E oppgraderer mockito-kotlin -> '1.7.10' // 1.7.0 skaper trøbbel for mockk: https://github.com/mockk/mockk/issues/832 - som igjen feiler tester i prefill-appen" This reverts commit 63c536cb3a... — committed to navikt/ep-personoppslag by dskarpas 2 years ago
- add tests for #832 — committed to aSemy/mockk by aSemy 2 years ago
- Merge pull request #861 from aSemy/fix/832-kt1_7-sealed-classes #832 Add tests for sealed classes — committed to mockk/mockk by Raibaz 2 years ago
- Properly handle sealed classes with Kotlin 1.7 and JDK 17 Attempt to fix https://github.com/mockk/mockk/issues/832 by making `ObjenesisInstantiator` work with a `KClass`. — committed to stuebingerb/mockk by stuebingerb 2 years ago
- Properly handle sealed classes with Kotlin 1.7 and JDK 17 Attempt to fix https://github.com/mockk/mockk/issues/832 by making `ObjenesisInstantiator` work with a `KClass`. — committed to stuebingerb/mockk by stuebingerb 2 years ago
- Properly handle sealed classes with Kotlin 1.7 and JDK 17 Attempt to fix https://github.com/mockk/mockk/issues/832 by making `ObjenesisInstantiator` work with a `KClass`. — committed to stuebingerb/mockk by stuebingerb 2 years ago
- Properly handle sealed classes with Kotlin 1.7 and JDK 17 Attempt to fix https://github.com/mockk/mockk/issues/832 by supporting sealed classes based on previous work done by @aSemy. — committed to stuebingerb/mockk by stuebingerb 2 years ago
- chore: bump the `mockk` version to 1.13.3 This is to address the sealed class issue: https://github.com/mockk/mockk/issues/832#issuecomment-2010320117. — committed to rudderlabs/rudder-sdk-android by 1abhishekpandey 2 months ago
- fix: unit test cases (#418) * chore: update java version to 17 in android module * chore: update java version to 17 in app module * chore: update java version to 17 in core module * chore: u... — committed to rudderlabs/rudder-sdk-android by 1abhishekpandey 2 months ago
Any update for this issue, is there any workaround?
I’m currently having issues publishing to oss.sonatype.org: https://issues.sonatype.org/browse/OSSRH-83030
As soon as this is resolved, I’ll publish a new release.
For those still looking at this issue like me, it was fixed in [this PR](https://github.com/mockk/mockk/pull/939), which was released in
1.13.3
.Thanks for providing more information! I’ve managed to reproduce it in the project tests in my refactoring PR #855.
The sealed class tests fail on 17 and 18, but pass on 11, as reported: https://github.com/mockk/mockk/actions/runs/2827760564 This is a step towards a solution…
For the sake of finishing the PR, I’ll disable the tests, then the problem can be investigated properly later.
Hey, I see from https://github.com/stuebingerb/mockk/pull/1 that all of the tests now pass, is this good to merge and be included in a new MockK release? My projects are waiting to upgrade to Kotlin 1.7 due to this mockk failure. Thanks!
@cliffred that matches what I am seeing locally with 1.13.2.
I wonder if this should be re-opened or a more specific issue raised?
@Raibaz may we have a new release please now that https://github.com/mockk/mockk/pull/916 got merged? Thanks in advance!
Done, v1.13.2 is out 😃
Apparently Kotlin 1.7 in combination with Java 17 uses Java sealed classes.
I compiled the following code with Kotlin 1.6 and 1.7 and then decompiled.
Kotlin 1.7 decompiled:
Kotlin 1.6 decompiled:
Tried but the test workflows are not so automated for first-time contributors 😉 https://github.com/mockk/mockk/pull/916
Great ideas, would you be able to give them a go?
@aSemy Hi.
Is there any update?
https://github.com/mockk/mockk/pull/916 attempts to fix the issue & hasn’t been merged yet.
But the code has not been merged in the main repo, right? The new release do not include the fixing code.
I’ve done research into this and I believe the problem is happening in
ObjensesisInstantiator
https://github.com/mockk/mockk/blob/6c571a651e74e66fe364b802a83849bfc7d6e802/modules/mockk-agent/src/jvmMain/kotlin/io/mockk/proxy/jvm/ObjenesisInstantiator.kt#L23-L43
Basically this tries to create a dummy, but valid, instance of a class. MockK can then keep track of this dummy instance, and where it is used. For primitives this is simple, MockK just creates a random primitive. For classes it’s a little more complicated, as MockK has to create an instance that conforms to that class’ requirements. Normally that’s okay, especially if the class is abstract or open. However, a sealed class looks like it’s abstract and open… except for it’s not.
So there are two problems:
ObjensesisInstantiator
a JavaClass<T>
is used, not aKClass<T>
, and so finding out if the class is sealed is more difficultI tried refactoring MockK to accept a
KClass<T>
, and so gain access to theisSealed
andsealedSubclasses
methods from Kotlin Reflect, but this requires a lot of refactoring, and this broke many other parts of the code.This problem isn’t impossible to overcome, but it’s not a quick fix. I’m sharing my work here, so hopefully someone else can take a look and either find another fix, or find something I missed.
Still problematic when upgrading to 1.12.7.
Due to issue #909, I used
mockk-jvm
in my project, but still encountered exception ofjava.lang.InstantiationError
when using Java 17/Kotlinsealed
class.Thanks for the suggestion, here a reproduction project: https://github.com/ThanksForAllTheFish/mockk832
Probably related: Getting the following error
Where code is:
and mock is:
Worked well in 1.6.10 / Java 17 with Mockk 1.12.2
1.13.2 only partly solves the issue:
This can probably be fixed in JvmSignatureValueGenerator with the same trick: Passing the first subclass to
instantiator.instantiate()
in case cls is sealed.Right, Java 17 added a sealed class feature, and I guess Kotlin is supporting that in the bytecode for Kotlin sealed classes when using Kotlin 1.7 with Java 17.
We encounter the same issue with sealed classes and interfaces with version
1.12.4
when we try to upgrade to Kotlin1.7.0
. Happens in thecoEvery
every
,verify
andcoVerify
blocks throwing ajava.lang.InstantiationError
.Same behaviour for simple
every {}
Updated to 1.12.8, still failed https://github.com/hantsy/mockk-issue832/actions/runs/3065819014/jobs/4950308126#step:4:42 I used
mockk-jvm
instead.Hey sorry about the delay, releasing 1.12.8 now.
Class.isSealed()
is only available on JVM 17+. I tried using it #915, but then MockK won’t compile on JVM 11: https://github.com/mockk/mockk/runs/8178643128?check_suite_focus=true#step:6:72Can you tell, why
java.lang.Class<T>
is not enought in this case?From my understanding, java.lang.Class also has this data (.isSealed() method and .getPermittedSubclasses()). And starting from Kotlin 1.7, when using 17+ target bytecode level, kotlin generates classes that are also sealed in JVM terms. For older bytecode versions current issue is not applicable, so doesn’t matter if JVM says that class is not sealed
@aSemy
I have cloned the project and the reason it works is because it is using Java 8 and not Java 17. https://github.com/mockk/mockk/blob/master/build.gradle https://github.com/mockk/mockk/blob/master/mockk/common/build.gradle.kts
@ygaller it seems related to
sealed class
.hey @aSemy I created a project recreating the issue https://github.com/odin-delrio/mockk-issue-832
The key point is that the JVM target for the kotlin compilation options must also be set to reproduce the issue (I’m using a build matrix for the tests):
Here you have the link to the failing build when using JDK 17 https://github.com/odin-delrio/mockk-issue-832/runs/7561938775?check_suite_focus=true#step:4:428
The same build with JDK 11 succeeded.