4ndro1d
4ndro1d

Reputation: 2976

Mocking Kotlin class not mockable

I have the following class

class GlobalFieldTitleMapper @Inject constructor() {

    fun toTitleMap(rawApiResponse: List<Map<String, Any>>): HashMap<Any, Any> {
        val titles = HashMap<Any, Any>()
        val globalHashMap = rawApiResponse.findLast { it.containsKey("global") }
        val globalValue = globalHashMap?.get("global") as Map<*, *>
        val boxAttributes = globalValue["boxattributes"]?.let { it as Map<*, *> }
        boxAttributes?.forEach { key, value ->
            titles[key!!] = (value as Map<*, *>)["boxtitle"]!!
        }
        return titles
    }
}

and I want to mock this method in a unit test:

@Mock
private lateinit var globalFieldTitleMapper: GlobalFieldTitleMapper

 @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
        recoRepo = RecommendationsRepo(recommendationsApi, SALESLINE, sessionInfo, productModuleMapper, globalFieldTitleMapper)  
      whenever(globalFieldTitleMapper.toTitleMap(any())).thenReturn(hashMapOf())
    }

But Mockito throws the following error:

org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class com.mediasaturn.app.home.recommendations.GlobalFieldTitleMapper.
Can not mock final classes with the following settings :
 - explicit serialization (e.g. withSettings().serializable())
 - extra interfaces (e.g. withSettings().extraInterfaces(...))

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 : org.mockito.exceptions.base.MockitoException: Could not modify all classes [class com.mediasaturn.app.home.recommendations.GlobalFieldTitleMapper]

    at com.mediasaturn.app.home.recommendations.RecommendationsRepoTest.setup(RecommendationsRepoTest.kt:42)
    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.RunBefores.evaluate(RunBefores.java:24)
    at com.mediasaturn.test.utils.JunitRxSchedulerRule$1.evaluate(JunitRxSchedulerRule.java:40)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    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:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    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.AppMainV2.main(AppMainV2.java:131)
Caused by: org.mockito.exceptions.base.MockitoException: Could not modify all classes [class com.mediasaturn.app.home.recommendations.GlobalFieldTitleMapper]
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:138)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:346)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:161)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:355)
    ... 30 more
Caused by: java.lang.IllegalStateException: 
Byte Buddy could not instrument all classes within the mock's type hierarchy

This problem should never occur for javac-compiled classes. This problem has been observed for classes that are:
 - Compiled by older versions of scalac
 - Classes that are part of the Android distribution
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:120)
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.mockClass(InlineBytecodeGenerator.java:97)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$1.call(TypeCachingBytecodeGenerator.java:37)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$1.call(TypeCachingBytecodeGenerator.java:34)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:138)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:346)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:161)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:355)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClass(TypeCachingBytecodeGenerator.java:32)
    at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMockType(InlineByteBuddyMockMaker.java:201)
    at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMock(InlineByteBuddyMockMaker.java:182)
    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:1729)
    at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:33)
    at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:16)
    at org.mockito.internal.configuration.IndependentAnnotationEngine.createMockFor(IndependentAnnotationEngine.java:38)
    at org.mockito.internal.configuration.IndependentAnnotationEngine.process(IndependentAnnotationEngine.java:62)
    at org.mockito.internal.configuration.InjectingAnnotationEngine.processIndependentAnnotations(InjectingAnnotationEngine.java:57)
    at org.mockito.internal.configuration.InjectingAnnotationEngine.process(InjectingAnnotationEngine.java:41)
    at org.mockito.MockitoAnnotations.initMocks(MockitoAnnotations.java:69)
    ... 30 more
Caused by: java.lang.IllegalStateException: Inconsistent frame length for public final java.lang.String com.mediasaturn.app.home.recommendations.GlobalFieldTitleMapper.toTitleMap(java.util.List): 0
    at net.bytebuddy.asm.Advice$StackMapFrameHandler$Default.translateFrame(Advice.java:1187)
    at net.bytebuddy.asm.Advice$StackMapFrameHandler$Default.translateFrame(Advice.java:1141)
    at net.bytebuddy.asm.Advice$AdviceVisitor.visitFrame(Advice.java:6636)
    at net.bytebuddy.jar.asm.ClassReader.a(Unknown Source)
    at net.bytebuddy.jar.asm.ClassReader.b(Unknown Source)
    at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
    at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:2910)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1628)
    at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:171)
    at net.bytebuddy.dynamic.scaffold.inline.AbstractInliningDynamicTypeBuilder.make(AbstractInliningDynamicTypeBuilder.java:92)
    at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:2560)
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.transform(InlineBytecodeGenerator.java:167)
    at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
    at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
    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:117)
    ... 50 more

I tried several approaches like setting explicit types (e.g. instead of Any in the parameter) and also temporarily changed the return type to String. All without success. I think Mockito does not care about the implementation of my method, but only about the signature of the function, but I still don't get what I am doing wrong. Is it the way I define the mock method - where I also tried to set an explicit object instead of hashMapOf(). Also without success. I am happy for every input.

EDIT: mock-maker-inline is enabled and working for other Kotlin classes

Also I tried to use the open keyword on my class - when I run the tests I get the following error

org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class com.mediasaturn.app.home.recommendations.GlobalFieldTitleMapper.

If you're not sure why you're getting this error, please report to the mailing list.


Java               : 1.8
JVM vendor name    : JetBrains s.r.o
JVM vendor version : 25.152-b01
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 1.8.0_152-release-1024-b01
JVM info           : mixed mode
OS name            : Mac OS X
OS version         : 10.12.6


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 : org.mockito.exceptions.base.MockitoException: Could not modify all classes [class com.mediasaturn.app.home.recommendations.GlobalFieldTitleMapper]

    at com.mediasaturn.app.home.recommendations.RecommendationsRepoTest.setup(RecommendationsRepoTest.kt:43)
    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.RunBefores.evaluate(RunBefores.java:24)
    at com.mediasaturn.test.utils.JunitRxSchedulerRule$1.evaluate(JunitRxSchedulerRule.java:40)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    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:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    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.AppMainV2.main(AppMainV2.java:131)
Caused by: org.mockito.exceptions.base.MockitoException: Could not modify all classes [class com.mediasaturn.app.home.recommendations.GlobalFieldTitleMapper]
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:138)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:346)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:161)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:355)
    ... 30 more
Caused by: java.lang.IllegalStateException: 
Byte Buddy could not instrument all classes within the mock's type hierarchy

This problem should never occur for javac-compiled classes. This problem has been observed for classes that are:
 - Compiled by older versions of scalac
 - Classes that are part of the Android distribution
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:120)
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.mockClass(InlineBytecodeGenerator.java:97)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$1.call(TypeCachingBytecodeGenerator.java:37)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$1.call(TypeCachingBytecodeGenerator.java:34)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:138)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:346)
    at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:161)
    at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:355)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClass(TypeCachingBytecodeGenerator.java:32)
    at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMockType(InlineByteBuddyMockMaker.java:201)
    at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMock(InlineByteBuddyMockMaker.java:182)
    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:1729)
    at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:33)
    at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:16)
    at org.mockito.internal.configuration.IndependentAnnotationEngine.createMockFor(IndependentAnnotationEngine.java:38)
    at org.mockito.internal.configuration.IndependentAnnotationEngine.process(IndependentAnnotationEngine.java:62)
    at org.mockito.internal.configuration.InjectingAnnotationEngine.processIndependentAnnotations(InjectingAnnotationEngine.java:57)
    at org.mockito.internal.configuration.InjectingAnnotationEngine.process(InjectingAnnotationEngine.java:41)
    at org.mockito.MockitoAnnotations.initMocks(MockitoAnnotations.java:69)
    ... 30 more
Caused by: java.lang.IllegalStateException: Inconsistent frame length for public final java.util.HashMap com.mediasaturn.app.home.recommendations.GlobalFieldTitleMapper.toGlobalField(java.util.List): 0
    at net.bytebuddy.asm.Advice$StackMapFrameHandler$Default.translateFrame(Advice.java:1187)
    at net.bytebuddy.asm.Advice$StackMapFrameHandler$Default.translateFrame(Advice.java:1141)
    at net.bytebuddy.asm.Advice$AdviceVisitor.visitFrame(Advice.java:6636)
    at net.bytebuddy.jar.asm.ClassReader.a(Unknown Source)
    at net.bytebuddy.jar.asm.ClassReader.b(Unknown Source)
    at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
    at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:2910)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1628)
    at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:171)
    at net.bytebuddy.dynamic.scaffold.inline.AbstractInliningDynamicTypeBuilder.make(AbstractInliningDynamicTypeBuilder.java:92)
    at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:2560)
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.transform(InlineBytecodeGenerator.java:167)
    at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
    at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
    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:117)
    ... 50 more

Upvotes: 4

Views: 5390

Answers (1)

DmitryBorodin
DmitryBorodin

Reputation: 4982

Mockito can mock non-open kotlin classes (or final java classes), but you need to turn this function on, since it may change behaviour and will affect mocking performance (which you most probably will not notice) This answer explains how to enable mocking for such cases.

Upvotes: 1

Related Questions