Attempt to mock a Kotlin-sealed class contained in another class fails when using mock-maker-inline
See original GitHub issueThis issue is raised when attempting to mock a sealed class that is contained in another class. Sealed classes are used in Kotlin to restrict class hierarchies.
class MyTest {
abstract class AbstractClass class private constructor()
sealed class SealedClass
@Test
fun mockAbstract() {
Mockito.mock(AbstractClass::class.java) // Succeeds
}
@Test
fun mockSealedClass() {
Mockito.mock(SealedClass::class.java) // Fails
}
}
Now the generated byte code for both classes is similar, except for the access flags:
// ================MyTest$AbstractClass.class =================
// class version 50.0 (50)
// access flags 0x421
public abstract class MyTest$AbstractClass {
// access flags 0x2
private <init>()V
L0
LINENUMBER 3 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this LMyTest$AbstractClass; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
@Lkotlin/Metadata;(mv={1, 1, 2}, bv={1, 0, 1}, k=1, d1={"\u0000\u000c\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\u0008\u0002\u0008&\u0018\u00002\u00020\u0001B\u0007\u0008\u0002\u00a2\u0006\u0002\u0010\u0002\u00a8\u0006\u0003"}, d2={"LMyTest$AbstractClass;", "", "()V", "test sources for module ui"})
- // access flags 0x409
- public static abstract INNERCLASS MyTest$AbstractClass MyTest AbstractClass
// compiled from: Test.kt
}
// ================MyTest$SealedClass.class =================
// class version 50.0 (50)
// access flags 0x421
public abstract class MyTest$SealedClass {
// access flags 0x2
private <init>()V
L0
LINENUMBER 5 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this LMyTest$SealedClass; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
@Lkotlin/Metadata;(mv={1, 1, 2}, bv={1, 0, 1}, k=1, d1={"\u0000\u000c\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\u0008\u0002\u00086\u0018\u00002\u00020\u0001B\u0007\u0008\u0002\u00a2\u0006\u0002\u0010\u0002\u00a8\u0006\u0003"}, d2={"LMyTest$SealedClass;", "", "()V", "test sources for module ui"})
+ // access flags 0x9
+ public static INNERCLASS MyTest$SealedClass MyTest SealedClass
// compiled from: Test.kt
}
When turning off mock-maker-inline
, both cases succeed.
It also does not occur when the sealed class is not nested.
The stacktrace:
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class MyTest$SealedClass.
If you're not sure why you're getting this error, please report to the mailing list.
Java : 1.8
JVM vendor name : Oracle Corporation
JVM vendor version : 25.76-b03
JVM name : OpenJDK 64-Bit Server VM
JVM version : 1.8.0_76-release-b03
JVM info : mixed mode
OS name : Linux
OS version : 4.4.0-45-generic
You are seeing this disclaimer because Mockito is configured to create inlined mocks.
You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.
Underlying exception : java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the class modifiers
at MyTest.mockSealedClass(Test.kt:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the class modifiers
at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:144)
at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:98)
at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.mockClass(InlineBytecodeGenerator.java:78)
at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$CachedBytecodeGenerator.getOrGenerateMockClass(TypeCachingBytecodeGenerator.java:87)
at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClass(TypeCachingBytecodeGenerator.java:34)
at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMockType(InlineByteBuddyMockMaker.java:164)
at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMock(InlineByteBuddyMockMaker.java:145)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:35)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:63)
at org.mockito.Mockito.mock(Mockito.java:1632)
at org.mockito.Mockito.mock(Mockito.java:1545)
... 28 more
Using Mockito version 2.2.9.
- The mockito message in the stacktrace have useful information, but it didn’t help
- The problematic code (if that’s possible) is copied here; Note that some configuration are impossible to mock via Mockito
- Provide versions (mockito / jdk / os / any other relevant information)
- Provide a Short, Self Contained, Correct (Compilable), Example of the issue (same as any question on stackoverflow.com)
- Read the contributing guide
Issue Analytics
- State:
- Created 7 years ago
- Comments:7 (5 by maintainers)
Top Results From Across the Web
How to mock final classes on Kotlin using Mockito 2 (KAD 23)
This means that if you want to mock a class (something that may be quite common in Java testing), you need to either...
Read more >Cannot mock final Kotlin class using Mockito 2 - Stack Overflow
The test fails when we try to initialise the mocks in the setUp() method. Please note I am using Mockito version 2 and...
Read more >Mockito cannot mock because : final class in Kotlin - MindOrks
In this blog, we will talk about how we can mock the final class using Mockito. By default, we can't mock the final...
Read more >[Solved]-Cannot mock final Kotlin class using Mockito 2-kotlin
PowerMock implements its own MockMaker which leads to incompatibility with Mockito mock-maker-inline, even if PowerMock is just added as a dependency and not ......
Read more >Kotlin Sealed Class - DigitalOcean
In Kotlin, Sealed Classes can be termed as Enum classes on steroids. Sealed classes allow us to create instances with different types, unlike ......
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I filed an issue at Kotlin: https://youtrack.jetbrains.com/issue/KT-14774
There is not much we can do as the Java reflection API does not correctly expose that the
Bar
class is abstract which is information we rely on. As said, Kotlin corrupts the class file’s inner class modifier which is ignored by the JVM’s runtime but applied by the reflection API. This puts our mocking engine off track.Kotlin 1.0.6 has been released with a fix for this.