mockito-kotlin
mockito-kotlin copied to clipboard
mockito-kotlin cannot verify higher-order functions in Robolectric Tests
@nhaarman , the area that I was trying to test was like this
val testArg = mock<(String) -> Unit>()
val result = someClass.doSomething(testArg)
verify(testArg).invoke(any())
And the error comes out as the folllwing
org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
-> at com.nhaarman.mockitokotlin2.VerificationKt.verify(Verification.kt:42)
Example of correct verification:
verify(mock).doSomething()
Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
at org.mockito.internal.junit.JUnitRule$1.evaluate(JUnitRule.java:44)
at com.hootsuite.testing.rule.RxJavaSchedulerRule$apply$1.evaluate(RxJavaSchedulerRule.kt:31)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.robolectric.internal.SandboxTestRunner$2.evaluate(SandboxTestRunner.java:253)
at org.robolectric.internal.SandboxTestRunner.runChild(SandboxTestRunner.java:130)
at org.robolectric.internal.SandboxTestRunner.runChild(SandboxTestRunner.java:42)
...
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)
We're seeing the same issue in our project. Our workaround is to create a test only interface that satisfies the higher-order function and can be verified. Using your example it'd look like:
val testCallback = mock<Callback>()
val result = someClass.doSomething(testCallback::invoke)
verify(testCallback).invoke(any())
interface Callback {
operator fun invoke(value: String)
}
Less than ideal but the only workaround we've found. Please let me know if there is an easier way.
@jachenry I will use your idea for now. I wish I had some hint from @nhaarman maybe what we can do to fix it as well :)
@jachenry I made these temporary SMI's to help. But these are also normally generated by Kotlin
interface Function0 {
operator fun invoke()
}
interface Function1<in P1, out R> {
operator fun invoke(p1: P1): R
}
interface Function2<in P1, in P2, out R> : Function<R> {
operator fun invoke(p1: P1, p2: P2): R
}
interface Function3<in P1, in P2, in P3, out R> : Function<R> {
operator fun invoke(p1: P1, p2: P2, p3: P3): R
}
val testArg = mock<(String) -> Unit>() val result = someClass.doSomething(testArg) verify(testArg).invoke(any())
Did you try changing any()
to anyString()
? This is a very common issue with Mockito-kotlin.
val testArg = mock<(String) -> Unit>()
val result = someClass.doSomething(testArg)
verify(testArg).invoke(anyString())
@bohsen I have not tried that. But I don't believe that's the root cause considering it also exists when verifying no-arg functions.
val somethingCallback = mock<() -> Unit>()
val result = someClass.doSomething(somethingCallback)
verify(somethingCallback).invoke()
For what it's worth, the behavior that I'm experiencing is that the first call to verify
in a test run succeeds, but the next call to verify
or mock
(in the same test or in the next test) will throw this exception. Using 1.6.0
I don't think this issue is specific to com.nhaarman.mockitokotlin2:mockito-kotlin
because the following test case reproduces the failure with only org.mockito:mockito-inline
and org.robolectric:robolectric
:
package com.example.test
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
@RunWith(AndroidJUnit4::class)
class Issue272Test {
@Suppress("UNCHECKED_CAST")
private val function = mock(Function1::class.java) as (String) -> Unit
@Test
fun test1() {
function.invoke("test")
verify(function).invoke("test")
}
@Test
fun test2() {
function.invoke("test")
verify(function).invoke("test")
}
}
Failure:
org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
-> at com.example.test.Issue272Test.test1(Issue272Test.kt:18)
Example of correct verification:
verify(mock).doSomething()
Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
at com.example.test.Issue272Test.<init>(Issue272Test.kt:13)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:217)
at org.robolectric.RobolectricTestRunner$HelperTestRunner.createTest(RobolectricTestRunner.java:534)
at org.junit.runners.BlockJUnit4ClassRunner$1.runReflectiveCall(BlockJUnit4ClassRunner.java:266)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:263)
at org.robolectric.internal.SandboxTestRunner$HelperTestRunner.methodBlock(SandboxTestRunner.java:322)
at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:252)
at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:89)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
I reported this issue to Robolectric with https://github.com/robolectric/robolectric/issues/5076. I haven't been able to rule out Mockito but that is working fine on both JVM (without Robolectric) and Android devices.
Here's a neat little workaround that you can do:
private class Callback : () -> Unit {
override fun invoke() = Unit
}
private val callback = mock<Callback>()
@Test
fun testFoo() {
...
verify(callback).invoke()
}
Another workaround is to use spy
:
val testArg = spy<(String) -> Unit> { _ -> }
val result = someClass.doSomething(testArg)
verify(testArg).invoke(any())
[EDIT] Seems that this trick doesn't work with mockito-kotlin 2.x (does work with 1.6.0). @jenzz solution suggested below works.
@jachenry @davidxiasc
Simply declaring a private interface Callback : () -> Unit
is sufficient as a workaround.