I am trying to write a unit test for my view model with help of Mockk.
@Test
fun `When loading the ResponseViewState isLoading`() {
val observer = spyk<Observer<ResponseViewState>>(Observer { })
puppiesViewModel.status_.observeForever(observer)
every {
fetchPuppiesUseCase.fetchPuppies()
} returns
Observable.just(Resource.Loading)
puppiesViewModel.fetchPuppies()
val slot = slot<ResponseViewState>()
verify { observer.onChanged(capture(slot)) }
assert(slot.captured.isLoading())
verify { fetchPuppiesUseCase.fetchPuppies() }
}
The error happens when I am creating the observer via spyk.
val observer = spyk<Observer<ResponseViewState>>(Observer { })
The error I am getting is
java.lang.NoClassDefFoundError: com/example/tink/PuppiesViewModelTest$$Lambda$61/0x0000000800176840
at jdk.internal.reflect.GeneratedSerializationConstructorAccessor4.newInstance(Unknown Source)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
at io.mockk.proxy.jvm.ObjenesisInstantiator.instanceViaObjenesis(ObjenesisInstantiator.kt:75)
at io.mockk.proxy.jvm.ObjenesisInstantiator.instance(ObjenesisInstantiator.kt:42)
at io.mockk.proxy.jvm.ProxyMaker.instantiate(ProxyMaker.kt:75)
at io.mockk.proxy.jvm.ProxyMaker.proxy(ProxyMaker.kt:42)
at io.mockk.impl.instantiation.JvmMockFactory.newProxy(JvmMockFactory.kt:34)
at io.mockk.impl.instantiation.AbstractMockFactory.newProxy$default(AbstractMockFactory.kt:29)
at io.mockk.impl.instantiation.AbstractMockFactory.spyk(AbstractMockFactory.kt:102)
at com.example.tink.PuppiesViewModelTest.createObserver(PuppiesViewModelTest.kt:99)
at com.example.tink.PuppiesViewModelTest.given loading state, when fetchPuppies called, then isLoading return true(PuppiesViewModelTest.kt:40)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
Caused by: java.lang.ClassNotFoundException: com.example.tink.PuppiesViewModelTest$$Lambda$61.0x0000000800176840
Any idea how to fix or maybe even a better approach for testing?
Mocking behavior of such a mock is connected to the special prototype mock denoted by anyConstructed<MockCls> (). There is one instance per class of such a prototype mock. Call recording also happens to the prototype mock.
Check the series of articles “Mocking is not rocket science” at Kt. Academy describing MockK from the very basics of mocking up to description of all advanced features. springmockk introduced in official Spring Boot Kotlin tutorial quarkus-mockk adds support for mocking beans in Quarkus. Documentation can be found here
There is one instance per class of such a prototype mock. Call recording also happens to the prototype mock. If no behavior for the function is specified, then the original function is executed. In case a class has more than one constructor, each can be mocked separately:
Spies allow you to mix mocks and real objects. Note: the spy object is a copy of the passed object. A relaxed mock is the mock that returns some simple value for all functions. This allows you to skip specifying behavior for each case, while still stubbing things you need. For reference types, chained mocks are returned.
It seems the change relates to Kotlin and was made at version 1.5. The change, KT-44912, relates to how the Kotlin compiler generates anonymous class implementing the SAM interface.
You can easily test this by changing your Kotlin version to 1.4.32 (latest 1.4.X).
An easy solution would be changing your code to the following:
val observer = createMockObserver()
@Suppress("ObjectLiteralToLambda") fun createMockObserver(): Observer<ResponseViewState> { val observer = object : Observer<ResponseViewState> { override fun onChanged(t: ResponseViewState?) { } } return spyk<Observer<ResponseViewState>>(observer) }
Alternatively, you can force the Kotlin compiler to use the pre-1.5 anonymous class generation by adding the following to your build.gradle
under the android
block:
afterEvaluate { compileDebugUnitTestKotlin { kotlinOptions { freeCompilerArgs += [ '-Xsam-conversions=class', ] } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With