mockito / mockito-kotlin

Using Mockito with Kotlin
MIT License
3.11k stars 201 forks source link

Support for AdditionalAnswers equivalent functions in OngoingStubbing.kt #343

Open sumitbhagwani opened 5 years ago

sumitbhagwani commented 5 years ago

Very common case when writing Mockito doAnswer is to return a value based on the input parameters.

I've added the following infix function many times in code (and its multiple arguments variants - 1 to 7 cover most cases I've ever touched)

/**
 * Sets a generic Answer for a unary function from a lambda.
 * Examples:
 * <pre>
 *   whenever(foo.hash(any())) doAnswer String::hashCode
 *   whenever(bar.addPunctuation(any())) doAnswer { s: String -> s + "!" }
 * </pre>
 */
@JvmName("doAnswer1")
infix fun <T, R> OngoingStubbing<R>.doAnswer(f: (T) -> R): OngoingStubbing<R> {
  return thenAnswer({ inv -> f(inv.arguments[0] as T) })
}

I was trying to create a PR to submit this upstream (along the lines of this commit) but I realized that it breaks the basic case when it can't decide which lambda to apply :

whenever(mocky.someFunc0()) doAnswer { 10 }

On one side, we should probably used doReturn 10 but on other side, both of them are functionally equivalent so should be allowed.

I can avoid this problem by changing the function name but it didn't felt right :-) Please let me know what is the best way for this to exist in mockito-kotlin lib. Thanks.

nhaarman commented 5 years ago

Interesting! This indeed breaks the 'no arguments' case though, and I'm not sure yet how I feel about using a different name to overcome this.

sumitbhagwani commented 5 years ago

I eventually submitted it internally with doTypeAnswer for the lack of better name. I would be happy to send a PR upstream for it.

/**
 * Sets a generic Answer for a unary function from a lambda.
 * Examples:
 * <pre>
 *   whenever(foo.hash(any())) doTypedAnswer String::hashCode
 *   whenever(bar.addPunctuation(any())) doTypedAnswer { s: String -> s + "!" }
 * </pre>
 */
@JvmName("doTypedAnswer1")
infix fun <T, R> OngoingStubbing<R>.doTypedAnswer(f: (T) -> R): OngoingStubbing<R> {
  return thenAnswer({ inv -> f(inv.arguments[0] as T) })
}