Mockk spky throwing NoClassDefFoundError

Viewed 1146

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?

1 Answers

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',
            ]
        }
    }
}
Related