Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Consider the following test:
It fails with:
Why?
Because the
foo
object is not a mock but it does call a mock in its implementation. That underlying mock call also returns anInt
, so Mockito happily stubs that method, instead. Yikes.We can't solve this in
when
/whenever
because it is not provided theMock
object, just the "result" of the (hopefully) method call to stub. In Kotlin, we can solve this inKStubbing
as a requirement on its argument. This highlights a pitfall when usingwhen
/whenever
that the developer must be certain they are stubbing aMock
and not a real implementation. To avoid this pitfall, we should use thedoReturn|Throw
family (see below) or immediately stubbing our mocks:How have I not seen this issue before?
Probably because you never put a real implementation into
when
/whenever
or your method was final, or the underlying method being picked up by Mockito was not returning the same value type. In these cases, Mockito will throw an error. Only in the last case, the error might be confusing:Mockito acknowledges this pitfall in the output of the error as tip 2:
Avoiding this pitfall
Mockito recommends against using
when
/whenever
and instead to use thedoReturn|Throw
family. This changes the stubbing flow to have thewhen
/whenever
take in the mock so that it can be verified:doReturn(42).whenever(foo).work()
Using our real implementation with this setup yields:
We can copy this check in
KStubbing
easily which gives developers the option to consistently construct their mocks:foo.stub { on { work() } doReturn 42 }
After this PR is merged, the above will throw a similar exception: