[Kotlin] Mocking fails when one suspend function calls other suspend function
See original GitHub issueWhen trying to mock class that has suspend
function that calls other suspend
function, it fails with exception:
Exception in thread "main" org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class com.myapp.SuspendableClass.
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 java.lang.Object, class com.myapp.SuspendableClass]
at com.myapp.DupaKt$main$1.doResume(dupa.kt:25)
at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:54)
at kotlinx.coroutines.experimental.DispatchTask.run(CoroutineDispatcher.kt:120)
at kotlinx.coroutines.experimental.EventLoopBase$QueuedRunnableTask.run(EventLoop.kt:189)
at kotlinx.coroutines.experimental.EventLoopBase.processNextEvent(EventLoop.kt:129)
at kotlinx.coroutines.experimental.BlockingCoroutine.joinBlocking(Builders.kt:225)
at kotlinx.coroutines.experimental.BuildersKt.runBlocking(Builders.kt:150)
at kotlinx.coroutines.experimental.BuildersKt.runBlocking$default(Builders.kt:142)
at com.myapp.DupaKt.main(dupa.kt:24)
Caused by: org.mockito.exceptions.base.MockitoException: Could not modify all classes [class java.lang.Object, class com.myapp.SuspendableClass]
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)
... 9 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.Mockito.mock(Mockito.java:1642)
... 9 more
Caused by: java.lang.IllegalStateException: public final java.lang.Object com.myapp.SuspendableClass.fetch(boolean,kotlin.coroutines.experimental.Continuation) is inconsistent at 1: java/lang/Object
at net.bytebuddy.asm.Advice$StackMapFrameHandler$Default.translateFrame(Advice.java:1200)
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)
... 23 more
Process finished with exit code 1
This is the simples example I could come up with to present this bug:
class SuspendableClass {
suspend fun fetch(updateOnly: Boolean = true): Int {
//uncomment to fix mocking
// runBlocking {
fetchConcrete()
// }
return 2
}
suspend fun fetchConcrete() = 1
}
fun main(args: Array<String>) = runBlocking<Unit> {
val mockClass = Mockito.mock(SuspendableClass::class.java)
Mockito.`when`(mockClass.fetch()).thenReturn(10)
MatcherAssert.assertThat(mockClass.fetch(), IsEqual(10))
Mockito.verify(mockClass).fetch()
}
Important notes:
- mock-maker-inline is used to mock
final
classes - mockito version: 2.8.47
- making class and all it’s methods
open
allows mocking - running inner
suspend
function insiderunBlocking
allows mocking
Issue Analytics
- State:
- Created 6 years ago
- Reactions:4
- Comments:28 (21 by maintainers)
Top Results From Across the Web
Mocking and stubbing suspend functions with MockK
How to easily stub or mock suspend function in Kotlin? MockK is the solution. See MockK examples for coroutines testing.
Read more >In kotlin, how do I mock a suspend function that wraps a ...
The above syntax fails with a mockito exception because it's expecting a matcher for the callback. org.mockito.exceptions.misusing.
Read more >Verifying suspending functions with Mockito (or alternatives)
I've run into trouble using Mockito to verify invocations of suspending functions. This appears to be related to how suspending functions ...
Read more >Coroutines and suspend functions | Mocking | MockK Guidebook
... function literals to create stubs, small changes are needed to stub suspend functions. MockK provides functions prefixed with co as equivalents to...
Read more >Testing Kotlin Coroutines - Kt. Academy
Testing suspending functions in most cases is not different from testing normal functions. Take a look at the fetchUserData below from FetchUserUseCase ....
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
The bytecode generation strategy was adjusted in Kotlin 1.1.4-3 which fixes this issue. See KT-19713 for details. I’ve updated PR #1165 with the test for this issue to Kotlin 1.1.4-3 and it passes. I suggest to close this issue and to merge PR #1165 into Mockito codebase to increase test coverage.
I’m getting this again with 2.17.0. It works with 2.16.0