mockito-kotlin icon indicating copy to clipboard operation
mockito-kotlin copied to clipboard

Verifying method invocation for methods with default parameters can fail unexpectedly

Open george-hawkins opened this issue 6 years ago • 0 comments

Verification of method invocations can fail in a non-obvious way if the method takes default arguments, e.g.:

val foo = mock<Foo>()
foo.beta(4)
verify(foo).beta()

Here we're trying to verify that beta() was called but it mysteriously fails, complaining about a different method altogether:

Wanted but not invoked:
foo.alpha();
-> at ...

On investigation, it's fairly clear why this happens - verify basically just primes things to look for the next method invoked on the mock that was passed to it. Above it seems obvious that this is beta() but the definition of Foo is:

interface Foo {
    fun alpha(): Int
    fun beta(s: Int = alpha())
}

I.e. calling beta() actually always results in alpha() being called on the mock first and hence the verification error referring to alpha().

@nhaarman has already pointed out that the solution to this is to prevent alpha() getting called by providing an argument:

verify(foo).beta(any())

So #174 already covered this but was closed due to lack of activity. Sorry to reopen the issue but the current behavior really is very confusing (I completely understand that this is a result of the interplay between Kotlin's default argument behavior and Mockito and not an issue introduced by mockito-kotlin).

At the moment verify(...) returns its argument, so one can't tell the difference between alpha() being called, to provide an argument, and beta() being called on the thing returned by verify(...). Perhaps it might be possible for verify(...) to wrap the thing it returns so the system can tell the difference between a method being called on that and a method being called on the original mock?

I guess there's already ticket against Mockito itself relating to this as one can get this confusing behavior in Java too? E.g. it doesn't seem obvious that the following shouldn't work in Java either:

verify(foo).beta(foo.alpha())

In Kotlin the issue becomes more pressing as you can't immediately see that anything is happening to foo between verify(...) returning and beta(...) being invoked. So something that's an inconvenience in Java becomes really non-obvious in Kotlin.

george-hawkins avatar Jul 17 '18 13:07 george-hawkins