From cda39a21a46d298eb398789abf2b0c612250766d Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Sun, 30 Nov 2025 18:02:08 +0100 Subject: [PATCH 1/6] Enhance and document stubbing API (apply infix and reified generics + apply whenever() with lambda arg as default in test cases). Also, deprecated methods onBlocking(methodCall), onGeneric(methodCall), wheneverBlocking(methodCall) to unify towards whenever(methodCall) and on(methodCall) as universal methods to stub any method call, being either synchronous, suspendable or with generics return type. --- .../kotlin/org/mockito/kotlin/KStubbing.kt | 228 ++++++++++-- .../org/mockito/kotlin/OngoingStubbing.kt | 295 +++++++++++++--- .../main/kotlin/org/mockito/kotlin/Stubber.kt | 333 ++++++++++++++++-- .../kotlin/internal/SuspendableAnswer.kt | 2 +- tests/src/test/kotlin/test/BDDMockitoTest.kt | 2 +- tests/src/test/kotlin/test/Classes.kt | 1 + .../test/CoroutinesOngoingStubbingTest.kt | 32 +- tests/src/test/kotlin/test/CoroutinesTest.kt | 32 +- tests/src/test/kotlin/test/MatchersTest.kt | 6 +- tests/src/test/kotlin/test/MockingTest.kt | 14 +- .../test/kotlin/test/OngoingStubbingTest.kt | 66 +++- tests/src/test/kotlin/test/StubberTest.kt | 116 ++++-- tests/src/test/kotlin/test/StubbingTest.kt | 132 ++++++- .../test/inline/UsingMockMakerInlineTest.kt | 4 +- 14 files changed, 1070 insertions(+), 193 deletions(-) diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KStubbing.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KStubbing.kt index 31a7c75c..b7c7c7d5 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KStubbing.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KStubbing.kt @@ -28,11 +28,18 @@ package org.mockito.kotlin import org.mockito.kotlin.internal.createInstance import kotlinx.coroutines.runBlocking import org.mockito.Mockito +import org.mockito.Mockito.`when` import org.mockito.exceptions.misusing.NotAMockException import org.mockito.stubbing.OngoingStubbing import org.mockito.stubbing.Stubber import kotlin.reflect.KClass +/** + * Stub a mock with given stubbing configuration. + * + * @param mock the mock to stub. + * @param stubbing the stubbing configuration to apply to the mock. + */ inline fun stubbing( mock: T, stubbing: KStubbing.(T) -> Unit @@ -40,6 +47,12 @@ inline fun stubbing( KStubbing(mock).stubbing(mock) } +/** + * Stub a mock with given stubbing configuration. + * + * @receiver the mock to stub. + * @param stubbing the stubbing configuration to apply to the mock. + */ inline fun T.stub(stubbing: KStubbing.(T) -> Unit): T { return apply { KStubbing(this).stubbing(this) } } @@ -49,44 +62,211 @@ class KStubbing(val mock: T) { if (!mockingDetails(mock).isMock) throw NotAMockException("Stubbing target is not a mock!") } - fun on(methodCall: R): OngoingStubbing = Mockito.`when`(methodCall) + /** + * Enables stubbing java methods or kotlin functions. Use it when you want the mock to return + * a particular value when particular method/function is being called. + * The kotlin function call to be stubbed can be either a synchronous or suspendable function. + * + * Simply put: "**on a call to** the x function **then** return y". + * + * Examples: + * + * ```kotlin + * stubbing(mock) { + * on (mock.someFunction()) doReturn 10 + * } + * ``` + * + * This function acts as an alias for [Mockito.when], as `when` is a keyword in kotlin and as such + * the Mockito method can only be called by wrapping the method name in backticks, e.g. `` `when` ``. + * To reduce the noise of backticks in your code, you can use this the function [whenever] instead. + * + * For more detail documentation, please refer to the Javadoc in the [Mockito] class. + * + * For stubbing Unit functions (or Java void methods) with throwables, see: [Mockito.doThrow]. + * + * @param methodCall method call to be stubbed. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. + */ + inline fun on(methodCall: R): OngoingStubbing = whenever(methodCall) - fun onGeneric(methodCall: T.() -> R?, c: KClass): OngoingStubbing { - val r = try { - mock.methodCall() + /** + * Enables stubbing java methods or kotlin functions. Use it when you want the mock to return + * a particular value when particular method/function is being called. + * The kotlin function call to be stubbed can be either a synchronous or suspendable function. + * + * Simply put: "**on a call to** the x function **then** return y". + * + * Examples: + * + * ```kotlin + * stubbing(mock) { + * on { mock.someFunction() } doReturn 10 + * } + * ``` + * + * This function acts as an alias for [Mockito.when], as `when` is a keyword in kotlin and as such + * the Mockito method can only be called by wrapping the method name in backticks, e.g. `` `when` ``. + * To reduce the noise of backticks in your code, you can use this the function [whenever] instead. + * + * For more detail documentation, please refer to the Javadoc in the [Mockito] class. + * + * For stubbing Unit functions (or Java void methods) with throwables, see: [Mockito.doThrow]. + * + * @param methodCall method call to be stubbed. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. + */ + fun on(methodCall: suspend T.() -> R): OngoingStubbing { + return try { + whenever { mock.methodCall() } } catch (e: NullPointerException) { + throw MockitoKotlinException( + "NullPointerException thrown when stubbing.\nThis may be due to two reasons:\n\t- The method you're trying to stub threw an NPE: look at the stack trace below;\n\t- You're trying to stub a generic method: try `onGeneric` instead.", + e + ) + } + } + + /** + * Enables stubbing java methods or kotlin functions with a generics return type [R]. + * Use it when you want the mock to return a particular value when particular method/function + * is being called. + * The kotlin function call to be stubbed can be either a synchronous or suspendable function. + * + * Simply put: "**on a call to** the x function **then** return y". + * + * Examples: + * + * ```kotlin + * interface GenericMethods { + * fun genericMethod(): T + * } + * + * mock> { + * onGeneric({ genericMethod() }, Int::class) doReturn 10 + * } + * ``` + * + * This function acts as an alias for [Mockito.when], as `when` is a keyword in kotlin and as such + * the Mockito method can only be called by wrapping the method name in backticks, e.g. `` `when` ``. + * To reduce the noise of backticks in your code, you can use this the function [whenever] instead. + * + * For more detail documentation, please refer to the Javadoc in the [Mockito] class. + * + * For stubbing Unit functions (or Java void methods) with throwables, see: [Mockito.doThrow]. + * + * @param methodCall method call to be stubbed. + * @param clazz the generics type. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. + */ + fun onGeneric(methodCall: suspend T.() -> R?, clazz: KClass): OngoingStubbing { + val r = try { + runBlocking { mock.methodCall() } + } catch (_: NullPointerException) { // An NPE may be thrown by the Kotlin type system when the MockMethodInterceptor returns a // null value for a non-nullable generic type. // We catch this NPE to return a valid instance. // The Mockito state has already been modified at this point to reflect // the wanted changes. - createInstance(c) + createInstance(clazz) + } - return Mockito.`when`(r) + return `when`(r)!! } - inline fun onGeneric(noinline methodCall: T.() -> R?): OngoingStubbing { + /** + * Enables stubbing java methods or kotlin functions with a generics return type [R]. + * Use it when you want the mock to return a particular value when particular method/function + * is being called. + * The kotlin function call to be stubbed can be either a synchronous or suspendable function. + * + * Simply put: "**on a call to** the x function **then** return y". + * + * Examples: + * + * ```kotlin + * interface GenericMethods { + * fun genericMethod(): T + * } + * + * mock> { + * onGeneric { genericMethod() } doReturn 10 + * } + * ``` + * + * This is a deprecated alias for [on]. Please use [on] instead. + * + * For more detailed documentation, please refer to [on]. + * + * @param methodCall method call to be stubbed. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. + */ + @Deprecated("Use on { mock.methodCall() } instead") + inline fun onGeneric(noinline methodCall: suspend T.() -> R?): OngoingStubbing { return onGeneric(methodCall, R::class) } - fun on(methodCall: T.() -> R): OngoingStubbing { - return try { - Mockito.`when`(mock.methodCall()) - } catch (e: NullPointerException) { - throw MockitoKotlinException( - "NullPointerException thrown when stubbing.\nThis may be due to two reasons:\n\t- The method you're trying to stub threw an NPE: look at the stack trace below;\n\t- You're trying to stub a generic method: try `onGeneric` instead.", - e - ) - } - } - - fun KStubbing.onBlocking( - m: suspend T.() -> R - ): OngoingStubbing { - return runBlocking { Mockito.`when`(mock.m()) } + /** + * Enables stubbing a (suspendable) kotlin functions. Use it when you want the mock to return + * a particular value when particular function is being called. + * The kotlin function call to be stubbed can be either a synchronous or suspendable function. + * + * Simply put: "**on a call to** the x function **then** return y". + * + * Examples: + * + * ```kotlin + * stubbing(mock) { + * onBlocking { mock.someFunction() } doReturn 10 + * } + * ``` + * + * This function acts as an alias for [Mockito.when], as `when` is a keyword in kotlin and as such + * the Mockito method can only be called by wrapping the method name in backticks, e.g. `` `when` ``. + * To reduce the noise of backticks in your code, you can use this function [on] instead. + * + * For more detail documentation, please refer to the Javadoc in the [Mockito] class. + * + * For stubbing Unit functions (or Java void methods) with throwables, see: [Mockito.doThrow]. + * + * This is a deprecated alias for [on]. Please use [on] instead. + * + * @param methodCall (regular or suspendable) lambda, wrapping the function call to be stubbed. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. + */ + @Deprecated("Use on { methodCall } instead") + fun KStubbing.onBlocking(methodCall: suspend T.() -> R): OngoingStubbing { + return runBlocking { `when`(mock.methodCall())!! } } - fun Stubber.on(methodCall: T.() -> Unit) { - this.`when`(mock).methodCall() + /** + * Sets the method call to be stubbed in a reverse manner, as part of a mock being created. + * You can reverse stub either synchronous as well as suspendable function calls. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * mock { + * doReturn("Test").on { stringResult() } + * } + * ``` + * Warning: Only one method call can be stubbed in the function. Subsequent method calls are ignored! + * + * This function acts as an alias for [whenever]. + * For more detailed documentation, please refer to [whenever]. + * + * @param methodCall (regular or suspendable) lambda, wrapping the method/function call to be stubbed. + */ + infix fun Stubber.on(methodCall: suspend T.() -> Unit) { + this.whenever(mock) { methodCall() } } } diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt index 39c6dcb2..7851bf34 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt @@ -28,6 +28,7 @@ package org.mockito.kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.runBlocking import org.mockito.Mockito +import org.mockito.Mockito.`when` import org.mockito.kotlin.internal.KAnswer import org.mockito.kotlin.internal.SuspendableAnswer import org.mockito.stubbing.Answer @@ -35,107 +36,309 @@ import org.mockito.stubbing.OngoingStubbing import kotlin.reflect.KClass /** - * Enables stubbing methods. Use it when you want the mock to return particular value when particular method is called. + * Enables stubbing java methods or kotlin functions. Use it when you want the mock to return + * a particular value when particular method/function is being called. + * The kotlin function call to be stubbed can be either a synchronous or suspendable function. * - * Alias for [Mockito.when]. + * Simply put: "**Whenever** the x function is being called **then** return y". + * + * Examples: + * + * ```kotlin + * whenever(mock.someFunction()) doReturn 10 + * + * //you can use flexible argument matchers, e.g: + * whenever(mock.someFunction(anyString())) doReturn 10 + * ``` + * + * This function acts as an alias for [Mockito.when], as `when` is a keyword in kotlin and as such + * the Mockito method can only be called by wrapping the method name in backticks, e.g. `` `when` ``. + * To reduce the noise of backticks in your code, you can use this the function [whenever] instead. + * + * For more detail documentation, please refer to the Javadoc in the [Mockito] class. + * + * For stubbing Unit functions (or Java void methods) with throwables, see: [Mockito.doThrow]. + * + * @param methodCall method call to be stubbed. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. + */ +inline fun whenever(methodCall: T): OngoingStubbing { + return `when`(methodCall)!! +} + +/** + * Enables stubbing java methods or kotlin functions. Use it when you want the mock to return + * a particular value when particular method/function is being called. + * The lambda with the method/function call to be stubbed can be either a synchronous (regular) + * or suspend lambda. + * + * **Warning**: Only the first method/function call in the lambda will be stubbed, other methods/functions calls are ignored! + * + * Simply put: "**Whenever** the x function is being called **then** return y". + * + * Examples: + * + * ```kotlin + * whenever { mock.someFunction() } doReturn 10 + * + * //you can use flexible argument matchers, e.g: + * whenever { mock.someFunction(anyString()) } doReturn 10 + * ``` + * + * This function acts as an alias for [Mockito.when], as `when` is a keyword in kotlin and as such + * the Mockito method can only be called by wrapping the method name in backticks, e.g. `` `when` ``. + * To reduce the noise of backticks in your code, you can use this the function [whenever] instead. + * Next to that, this function will take care of handling with suspend lambda, to ease the stubbing + * of a suspendable function call. + * + * For more detailed documentation, please refer to the Javadoc in the [Mockito] class. + * + * For stubbing Unit functions (or Java void methods), see: [Mockito.doThrow]. + * + * @param methodCall (regular or suspendable) lambda, wrapping the method/function call to be stubbed. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. */ -@Suppress("NOTHING_TO_INLINE") -inline fun whenever(methodCall: T): OngoingStubbing { - return Mockito.`when`(methodCall)!! +fun whenever(methodCall: suspend CoroutineScope.() -> T): OngoingStubbing { + return runBlocking { `when`(methodCall())!! } } /** - * Enables stubbing suspending methods. Use it when you want the mock to return particular value when particular suspending method is called. + * Enables stubbing a (suspendable) kotlin functions. Use it when you want the mock to return + * a particular value when particular function is being called. + * The lambda with the function call to be stubbed can be either a synchronous (regular) + * or suspend lambda. + * + * This is a deprecated alias for [whenever]. Please use [whenever] instead. * - * Warning: Only one method call can be stubbed in the function. - * other method calls are ignored! + * For more detailed documentation, please refer to [whenever]. + * + * @param methodCall (regular or suspendable) lambda, wrapping the function call to be stubbed. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. */ +@Deprecated("Use whenever { mock.methodCall() } instead") fun wheneverBlocking(methodCall: suspend CoroutineScope.() -> T): OngoingStubbing { - return runBlocking { Mockito.`when`(methodCall()) } + return whenever(methodCall) } /** - * Sets a return value to be returned when the method is called. + * Sets a return value to be returned when the method/function is called. E.g: + * + * ```kotlin + * whenever { mock.someMethod() } doReturn 10 + * ``` * - * Alias for [OngoingStubbing.thenReturn]. + * This function acts as an alias for Mockito's [OngoingStubbing.thenReturn], adding the infix + * functionality and extended type inference to it. + * + * @param value return value for the method/function invocation. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. */ -infix fun OngoingStubbing.doReturn(t: T): OngoingStubbing { - return thenReturn(t) +inline infix fun OngoingStubbing.doReturn(value: T): OngoingStubbing { + return thenReturn(value) } /** - * Sets consecutive return values to be returned when the method is called. + * Sets a return values to be returned when the method/function is called consecutively. E.g: + * + * ```kotlin + * whenever { mock.someMethod() }.doReturn(10, 20) + * ``` + * You can specify [values] to be returned on consecutive invocations. + * In that case the last value determines the behavior of further consecutive invocations. * - * Alias for [OngoingStubbing.thenReturn]. + * This function acts as an alias for Mockito's [OngoingStubbing.thenReturn], adding extended + * type inference to it. + * + * @param value return value for the first method/function invocation. + * @param values return values for the next method/function invocations. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. */ -fun OngoingStubbing.doReturn(t: T, vararg ts: T): OngoingStubbing { - return thenReturn(t, *ts) +inline fun OngoingStubbing.doReturn(value: T, vararg values: T): OngoingStubbing { + return doReturnConsecutively(value, *values) } /** - * Sets consecutive return values to be returned when the method is called. + * Sets a return values to be returned when the method/function is called consecutively. E.g: + * + * ```kotlin + * whenever { mock.someMethod() }.doReturnConsecutively(10, 20) + * ``` + * You can specify [values] to be returned on consecutive invocations. + * In that case the last value determines the behavior of further consecutive invocations. + * + * This function acts as an alias for Mockito's [OngoingStubbing.thenReturn], adding extended + * type inference to it. + * + * @param value return value for the first method/function invocation. + * @param values return values for the next method/function invocations. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. */ -inline infix fun OngoingStubbing.doReturnConsecutively(ts: List): OngoingStubbing { - return thenReturn( - ts[0], - *ts.drop(1).toTypedArray() - ) +inline fun OngoingStubbing.doReturnConsecutively(value: T, vararg values: T): OngoingStubbing { + return doReturnConsecutively(listOf(value, *values)) } /** - * Sets Throwable objects to be thrown when the method is called. + * Sets a return values to be returned when the method/function is called consecutively. E.g: + * + * ```kotlin + * whenever { mock.someMethod() } doReturnConsecutively listOf(10, 20) + * ``` * - * Alias for [OngoingStubbing.thenThrow]. + * The last value in [values] determines the behavior of further consecutive invocations. + * + * This function acts as an alias for Mockito's [OngoingStubbing.thenReturn], adding the infix + * functionality and extended type inference to it. + * + * @param values return values for the consecutive method/function invocations. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. */ -infix fun OngoingStubbing.doThrow(t: Throwable): OngoingStubbing { - return thenThrow(t) +inline infix fun OngoingStubbing.doReturnConsecutively(values: List): OngoingStubbing { + return thenReturn(values.first(), *values.drop(1).toTypedArray()) } /** - * Sets Throwable objects to be thrown when the method is called. + * Sets Throwable instance to be thrown when the method/function is called. E.g: + * + * ```kotlin + * whenever { mock.someFunction() } doThrow RuntimeException() + * ``` + * + * This function acts as an alias for Mockito's [OngoingStubbing.thenThrow], adding the + * infix functionality to it. * - * Alias for [OngoingStubbing.doThrow]. + * @param throwable to be thrown on method/function invocations. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. */ -fun OngoingStubbing.doThrow( - t: Throwable, - vararg ts: Throwable -): OngoingStubbing { - return thenThrow(t, *ts) +infix fun OngoingStubbing.doThrow(throwable: Throwable): OngoingStubbing { + return thenThrow(throwable) } /** - * Sets a Throwable type to be thrown when the method is called. + * Sets Throwable instance(s) to be thrown when the method/function is called consecutively. E.g: + * + * ```kotlin + * whenever { mock.someFunction() }.doThrow(RuntimeException(), IOException()) + * ``` + * + * You can specify [throwables] to be thrown for consecutive invocations. + * In that case the last throwable determines the behavior of further consecutive invocations. + * + * This function acts as an alias for Mockito's [OngoingStubbing.thenThrow]. + * + * @param throwable to be thrown on the first method/function invocation. + * @param throwables to be thrown on the next method/function invocations. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. */ -infix fun OngoingStubbing.doThrow(t: KClass): OngoingStubbing { - return thenThrow(t.java) +fun OngoingStubbing.doThrow(throwable: Throwable, vararg throwables: Throwable): OngoingStubbing { + return thenThrow(throwable, *throwables) } /** - * Sets Throwable classes to be thrown when the method is called. + * Sets a Throwable type to be thrown when the method/function is called. E.g: + * + * ```kotlin + * whenever { mock.someFunction() } doThrow IllegalArgumentException::class + * ``` + * + * This function acts as an alias for Mockito's [OngoingStubbing.thenThrow], accepting the + * throwable type as [KClass] and adding the infix functionality to it. + * + * @param throwableType to be thrown on the method/function invocation. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. */ -fun OngoingStubbing.doThrow( - t: KClass, - vararg ts: KClass -): OngoingStubbing { - return thenThrow(t.java, *ts.map { it.java }.toTypedArray()) +infix fun OngoingStubbing.doThrow(throwableType: KClass): OngoingStubbing { + return thenThrow(throwableType.java) } /** - * Sets a generic Answer for the method. + * Sets a Throwable type to be thrown when the method is called consecutively. E.g: + * + * ```kotlin + * whenever { mock.someFunction() }.doThrow(IllegalArgumentException::class, NullPointerException::class) + * ``` * - * Alias for [OngoingStubbing.thenAnswer]. + * You can specify [throwableTypes] to be thrown for consecutive invocations. + * In that case the last throwable type determines the behavior of further consecutive invocations. + * + * This function acts as an alias for Mockito's [OngoingStubbing.thenThrow], accepting the + * throwable types as [KClass]. + * + * @param throwableType to be thrown on the first method/function invocation. + * @param throwableTypes to be thrown on the next method/function invocations. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. + */ +fun OngoingStubbing.doThrow(throwableType: KClass, vararg throwableTypes: KClass): OngoingStubbing { + return thenThrow(throwableType.java, *throwableTypes.map { it.java }.toTypedArray()) +} + +/** + * Sets a generic answer for when the method/function is called. E.g: + * + * ```kotlin + * val answer = Answer { "result" } + * whenever { mock.someFunction() } doAnswer answer + * ``` + * + * This function acts as an alias for Mockito's [OngoingStubbing.thenAnswer], adding the infix + * functionality to it. + * + * @param answer to be applied on the method/function invocation. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. */ infix fun OngoingStubbing.doAnswer(answer: Answer<*>): OngoingStubbing { return thenAnswer(answer) } /** - * Sets a generic Answer for the method using a lambda. + * Sets an answer for when the method/function is called, specified by a lambda. E.g: + * + * ```kotlin + * whenever { mock.someFunction() } doAnswer { "result" } + * ``` + * + * This function acts as an alias for Mockito's [OngoingStubbing.thenAnswer], adding the infix + * functionality to it. + * + * @param answer to be applied on the method/function invocation. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. */ infix fun OngoingStubbing.doAnswer(answer: (KInvocationOnMock) -> T?): OngoingStubbing { return thenAnswer(KAnswer(answer)) } +/** + * Sets an answer for when the suspendable function is called, specified by a suspendable lambda. E.g: + * + * ```kotlin + * whenever { mock.someFunction() } doAnswer { + * delay(1) + * "result" + * } + * ``` + * + * This function acts as an alias for Mockito's [OngoingStubbing.thenAnswer], adding the infix + * functionality to it. + * Also, this function will wrap the answer lambda in a SuspendableAnswer object, to wire the + * suspendable lambda properly into the Kotlin's coroutine context on invocation of the stubbed + * suspendable function. + * + * @param answer to be applied on the suspendable function invocation. + * @return OngoingStubbing object used to stub fluently. + * ***Do not*** create a reference to this returned object. + */ infix fun OngoingStubbing.doSuspendableAnswer(answer: suspend (KInvocationOnMock) -> T?): OngoingStubbing { return thenAnswer(SuspendableAnswer(answer)) } diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt index 9b521de4..cdedb2a5 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt @@ -27,56 +27,345 @@ package org.mockito.kotlin import kotlinx.coroutines.runBlocking import org.mockito.Mockito -import org.mockito.invocation.InvocationOnMock import org.mockito.kotlin.internal.SuspendableAnswer -import org.mockito.stubbing.OngoingStubbing import org.mockito.stubbing.Stubber import kotlin.reflect.KClass -fun doAnswer(answer: (InvocationOnMock) -> T?): Stubber { - return Mockito.doAnswer { answer(it) }!! +/** + * Sets a generic answer, specified with a lambda, to be applied in reverse stubbing. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doAnswer { "result" }.whenever(mock).someMethod() + * ``` + * + * This function acts as an alias for Mockito's [Mockito.doAnswer]. + * + * See examples in javadoc for [Mockito] class + * + * @param answer to answer to apply when the stubbed method/function is called. + * @return Stubber object used to stub fluently. + */ +fun doAnswer(answer: (KInvocationOnMock) -> T?): Stubber { + return Mockito.doAnswer { answer.invoke(KInvocationOnMock(it)) } } +/** + * Sets a generic answer, specified with a suspendable lambda, to be applied in reverse stubbing. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doSuspendableAnswer { + * delay(1) + * "result" + * }.whenever(mock).someMethod() + * ``` + * + * This function acts as an alias for Mockito's [Mockito.doAnswer], this function will also wrap + * the answer lambda in a SuspendableAnswer object, to wire the suspendable lambda properly + * into the Kotlin's coroutine context on invocation of the stubbed suspendable function. + * + * See examples in javadoc for [Mockito] class + * + * @param answer to answer to apply when the stubbed method/function is called. + * @return Stubber object used to stub fluently. + */ fun doSuspendableAnswer(answer: suspend (KInvocationOnMock) -> T?): Stubber { return Mockito.doAnswer(SuspendableAnswer(answer)) } +/** + * Sets to call the real implementation of a method in a mock, to be applied in reverse stubbing. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doCallRealMethod().whenever(mock).someMethod() + * ``` + * + * This function acts as an alias for Mockito's [Mockito.doCallRealMethod]. + * + * See examples in javadoc for [Mockito] class + * + * @return Stubber object used to stub fluently. + */ fun doCallRealMethod(): Stubber { return Mockito.doCallRealMethod()!! } +/** + * Sets to do nothing, to be applied in reverse stubbing. + * This comes handy is some rare cases, like: + * - stubbing consecutive calls with different behavior + * - when spying a real object, suppress the real implementation of the spied object + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doNothing().whenever(mock).someMethod() + * ``` + * + * This function acts as an alias for Mockito's [Mockito.doNothing]. + * + * See examples in javadoc for [Mockito] class + * + * @return Stubber object used to stub fluently. + */ fun doNothing(): Stubber { - return Mockito.doNothing()!! + return Mockito.doNothing() } +/** + * Sets to return the given value, to be applied in reverse stubbing. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doReturn(10).whenever(mock).someMethod() + * ``` + * + * This function acts as an alias for Mockito's [Mockito.doReturn]. + * + * See examples in javadoc for [Mockito] class + * + * @param value return value for the method/function invocation. + * @return Stubber object used to stub fluently. + */ fun doReturn(value: Any?): Stubber { - return Mockito.doReturn(value)!! + return Mockito.doReturn(value) +} + +/** + * Sets to return the given values on consecutive invocations, to be applied in reverse stubbing. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doReturn(10, 20).whenever(mock).someMethod() + * ``` + * + * This function acts as an alias for Mockito's [Mockito.doReturn]. + * + * See examples in javadoc for [Mockito] class + * + * @param value return value for the first method/function invocation. + * @param values return values for the next method/function invocations. + * @return Stubber object used to stub fluently. + */ +fun doReturn(value: Any?, vararg values: Any?): Stubber { + return Mockito.doReturn(value, *values) +} + +/** + * Sets a Throwable to be thrown, to be applied in reverse stubbing. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doThrow(IllegalArgumentException()).whenever(mock).someMethod() + * ``` + * + * This function acts as an alias for Mockito's [Mockito.doThrow]. + * + * See examples in javadoc for [Mockito] class + * + * @param throwable to be thrown on the method/function invocation. + * @return Stubber object used to stub fluently. + */ +fun doThrow(throwable: Throwable): Stubber { + return Mockito.doThrow(throwable) } -fun doReturn(toBeReturned: Any?, vararg toBeReturnedNext: Any?): Stubber { - return Mockito.doReturn( - toBeReturned, - *toBeReturnedNext - )!! +/** + * Sets Throwables to be thrown consecutively, to be applied in reverse stubbing. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doThrow(RuntimeException(), IOException()).whenever(mock).someMethod() + * ``` + * + * This function acts as an alias for Mockito's [Mockito.doThrow]. + * + * You can specify [throwables] to be thrown for consecutive invocations. + * In that case the last throwable determines the behavior of further consecutive invocations. + * + * See examples in javadoc for [Mockito] class + * + * @param throwable to be thrown on the first method/function invocation. + * @param throwables to be thrown on the next method/function invocations. + * @return Stubber object used to stub fluently. + */ +fun doThrow(throwable: Throwable, vararg throwables: Throwable): Stubber { + return Mockito.doThrow(throwable, *throwables) } -fun doThrow(toBeThrown: KClass): Stubber { - return Mockito.doThrow(toBeThrown.java)!! +/** + * Sets a Throwable types to be thrown, to be applied in reverse stubbing. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doThrow(IllegalArgumentException::class).whenever(mock).someMethod() + * ``` + * + * This function acts as an alias for Mockito's [Mockito.doThrow]. + * + * See examples in javadoc for [Mockito] class + * + * @param throwableType to be thrown on the method/function invocation. + * @return Stubber object used to stub fluently. + */ +fun doThrow(throwableType: KClass): Stubber { + return Mockito.doThrow(throwableType.java) } -fun doThrow(vararg toBeThrown: Throwable): Stubber { - return Mockito.doThrow(*toBeThrown)!! +/** + * Sets a Throwable types to be thrown consecutively, to be applied in reverse stubbing. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doThrow(IllegalArgumentException::class, NullPointerException::class) + * .whenever(mock) + * .someMethod() + * ``` + * + * This function acts as an alias for Mockito's [Mockito.doThrow]. + * + * You can specify [throwableTypes] to be thrown for consecutive invocations. + * In that case the last throwable type determines the behavior of further consecutive invocations. + * + * See examples in javadoc for [Mockito] class + * + * @param throwableType to be thrown on the first method/function invocation. + * @param throwableTypes to be thrown on the next method/function invocations. + * @return Stubber object used to stub fluently. + */ +fun doThrow(throwableType: KClass, vararg throwableTypes: KClass): Stubber { + return Mockito.doThrow( + throwableType.java, + *throwableTypes.map { it.java }.toTypedArray() + ) } -fun Stubber.whenever(mock: T) = `when`(mock) +/** + * Sets the mock to apply the reverse stubbing on. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doReturn(10).whenever(mock).someMethod() + * ``` + * + * This function acts as an alias for Mockito's [Mockito.doReturn]. + * + * See examples in javadoc for [Mockito] class + * + * @param mock the mock to stub the method/function call on. + * @return the mock used to stub the method/function call fluently. + */ +fun Stubber.whenever(mock: T) = `when`(mock)!! + +/** + * Sets the mock and the method call to be stubbed. + * With this version of whenever you can reverse stub either synchronous as well as suspendable + * function calls. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doReturn(10).whenever(mock) { someMethod() } + * ``` + * Warning: Only one method call can be stubbed in the function. Subsequent method calls are ignored! + * + * This function acts as an alias for Mockito's [Mockito.`when`]. + * + * See examples in javadoc for [Mockito] class + * + * @param mock the mock to stub the method/function call on. + * @param methodCall (regular or suspendable) lambda, wrapping the method/function call to be stubbed. + */ +fun Stubber.whenever(mock: T, methodCall: suspend T.() -> Unit) { + whenever(mock).let { + runBlocking { it.methodCall() } + } +} /** - * Alias for when with suspending function + * Sets the mock and the method call to be stubbed. + * With this version of whenever you can reverse stub either synchronous as well as suspendable + * function calls. + * + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as + * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to + * problems in case of void methods (or Unit functions): the java compiler does not like void + * methods inside brackets... + * + * Example: + * ```kotlin + * doReturn(10).wheneverBlocking(mock) { someMethod() } + * ``` + * Warning: Only one method call can be stubbed in the function. Subsequent method calls are ignored! + * + * This function is an alias for [whenever]. + * + * See examples in javadoc for [Mockito] class * - * Warning: Only one method call can be stubbed in the function. - * Subsequent method calls are ignored! + * @param mock the mock to stub the method/function call on. + * @param methodCall (regular or suspendable) lambda, wrapping the method/function call to be stubbed. */ -fun Stubber.wheneverBlocking(mock: T, f: suspend T.() -> Unit) { - val m = whenever(mock) - runBlocking { m.f() } +@Deprecated("Use whenever(mock) { methodCall() } instead") +fun Stubber.wheneverBlocking(mock: T, methodCall: suspend T.() -> Unit) { + whenever(mock, methodCall) } diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/internal/SuspendableAnswer.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/internal/SuspendableAnswer.kt index 239be2cd..62a96d75 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/internal/SuspendableAnswer.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/internal/SuspendableAnswer.kt @@ -40,7 +40,7 @@ internal class SuspendableAnswer( private val body: suspend (KInvocationOnMock) -> T? ) : Answer { override fun answer(invocation: InvocationOnMock?): T { - //all suspend functions/lambdas has Continuation as the last argument. + //all suspendable functions/lambdas have Continuation as the last argument. //InvocationOnMock does not see last argument val rawInvocation = invocation as InterceptedInvocation val continuation = rawInvocation.rawArguments.last() as Continuation diff --git a/tests/src/test/kotlin/test/BDDMockitoTest.kt b/tests/src/test/kotlin/test/BDDMockitoTest.kt index 457bf406..13be9d53 100644 --- a/tests/src/test/kotlin/test/BDDMockitoTest.kt +++ b/tests/src/test/kotlin/test/BDDMockitoTest.kt @@ -120,7 +120,7 @@ class BDDMockitoTest { fun then() { /* Given */ val mock = mock() - whenever(mock.stringResult()).thenReturn("Test") + whenever { mock.stringResult() }.thenReturn("Test") /* When */ mock.stringResult() diff --git a/tests/src/test/kotlin/test/Classes.kt b/tests/src/test/kotlin/test/Classes.kt index bfc719ed..b2a9582e 100644 --- a/tests/src/test/kotlin/test/Classes.kt +++ b/tests/src/test/kotlin/test/Classes.kt @@ -166,6 +166,7 @@ abstract class NonThrowingConstructorWithArgument { interface GenericMethods { fun genericMethod(): T fun nullableReturnType(): T? + suspend fun suspendableGenericMethod(): T } class ThrowableClass(cause: Throwable) : Throwable(cause) diff --git a/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt b/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt index 244597cb..fc089057 100644 --- a/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt +++ b/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt @@ -21,7 +21,7 @@ class CoroutinesOngoingStubbingTest { fun `should stub suspendable function call`() { /* Given */ val mock = mock { - onBlocking { stringResult() } doReturn "A" + on { stringResult() } doReturn "A" } /* When */ @@ -35,7 +35,7 @@ class CoroutinesOngoingStubbingTest { fun `should stub suspendable function call within a coroutine scope`() = runTest { /* Given */ val mock = mock { - onBlocking { stringResult() } doReturn "A" + on { stringResult() } doReturn "A" } /* When */ @@ -49,7 +49,7 @@ class CoroutinesOngoingStubbingTest { fun `should stub consecutive suspendable function calls`() { /* Given */ val mock = mock { - onBlocking { stringResult() }.doReturn("A", "B", "C") + on { stringResult() }.doReturn("A", "B", "C") } /* When */ @@ -67,7 +67,7 @@ class CoroutinesOngoingStubbingTest { fun `should stub consecutive suspendable function calls by a list of answers`() { /* Given */ val mock = mock { - onBlocking { stringResult() } doReturnConsecutively listOf("A", "B", "C") + on { stringResult() } doReturnConsecutively listOf("A", "B", "C") } /* When */ @@ -99,7 +99,7 @@ class CoroutinesOngoingStubbingTest { fun `should stub builder method returning mock itself via answer`() { /* Given */ val mock = mock { - onBlocking { builderMethod() } doAnswer Mockito.RETURNS_SELF + on { builderMethod() } doAnswer Mockito.RETURNS_SELF } /* When */ @@ -113,7 +113,7 @@ class CoroutinesOngoingStubbingTest { fun `should stub builder method returning mock itself`() { /* Given */ val mock = mock { mock -> - onBlocking { builderMethod() } doReturn mock + on { builderMethod() } doReturn mock } /* When */ @@ -127,7 +127,7 @@ class CoroutinesOngoingStubbingTest { fun `should stub suspendable function call with nullable result`() { /* Given */ val mock = mock { - onBlocking { nullableStringResult() } doReturn "Test" + on { nullableStringResult() } doReturn "Test" } /* When */ @@ -141,7 +141,7 @@ class CoroutinesOngoingStubbingTest { fun `should throw exception instance on suspendable function call`() { /* Given */ val mock = mock { - onBlocking { builderMethod() } doThrow IllegalArgumentException() + on { builderMethod() } doThrow IllegalArgumentException() } /* When, Then */ @@ -156,7 +156,7 @@ class CoroutinesOngoingStubbingTest { fun `should throw exception class on suspendable function call`() { /* Given */ val mock = mock { - onBlocking { builderMethod() } doThrow IllegalArgumentException::class + on { builderMethod() } doThrow IllegalArgumentException::class } /* When, Then */ @@ -171,7 +171,7 @@ class CoroutinesOngoingStubbingTest { fun `should throw exception instances on consecutive suspendable function calls`() { /* Given */ val mock = mock { - onBlocking { builderMethod() }.doThrow( + on { builderMethod() }.doThrow( IllegalArgumentException(), UnsupportedOperationException() ) @@ -192,7 +192,7 @@ class CoroutinesOngoingStubbingTest { fun `should throw exception classes on consecutive suspendable function calls`() { /* Given */ val mock = mock { - onBlocking { builderMethod() }.doThrow( + on { builderMethod() }.doThrow( IllegalArgumentException::class, UnsupportedOperationException::class ) @@ -214,7 +214,7 @@ class CoroutinesOngoingStubbingTest { /* Given */ val answer: Answer = Answer { "result" } val mock = mock { - onBlocking { stringResult() } doAnswer answer + on { stringResult() } doAnswer answer } /* When */ @@ -229,7 +229,7 @@ class CoroutinesOngoingStubbingTest { fun `should stub suspendable function call with result from lambda`() { /* Given */ val mock = mock { - onBlocking { stringResult() } doAnswer { "result" } + on { stringResult() } doAnswer { "result" } } /* When */ @@ -243,7 +243,7 @@ class CoroutinesOngoingStubbingTest { fun `should stub suspendable function call with result from lambda with argument`() { /* Given */ val mock = mock { - onBlocking { stringResult(any()) } doAnswer { "${it.arguments[0]}-result" } + on { stringResult(any()) } doAnswer { "${it.arguments[0]}-result" } } /* When */ @@ -257,7 +257,7 @@ class CoroutinesOngoingStubbingTest { fun `should stub suspendable function call with result from lambda with deconstructed argument`() { /* Given */ val mock = mock { - onBlocking { stringResult(any()) } doAnswer { (s: String) -> "$s-result" } + on { stringResult(any()) } doAnswer { (s: String) -> "$s-result" } } /* When */ @@ -271,7 +271,7 @@ class CoroutinesOngoingStubbingTest { fun `should stub suspendable function call with result from lambda with deconstructed arguments`() { /* Given */ val mock = mock { - onBlocking { stringResult(any(), any()) } doAnswer { (a: String, b: String) -> + on { stringResult(any(), any()) } doAnswer { (a: String, b: String) -> "$a + $b" } } diff --git a/tests/src/test/kotlin/test/CoroutinesTest.kt b/tests/src/test/kotlin/test/CoroutinesTest.kt index 044402d8..1612b4be 100644 --- a/tests/src/test/kotlin/test/CoroutinesTest.kt +++ b/tests/src/test/kotlin/test/CoroutinesTest.kt @@ -23,7 +23,6 @@ import org.mockito.kotlin.spy import org.mockito.kotlin.verify import org.mockito.kotlin.verifyBlocking import org.mockito.kotlin.whenever -import org.mockito.kotlin.wheneverBlocking import java.util.* class CoroutinesTest { @@ -32,7 +31,7 @@ class CoroutinesTest { fun stubbingSuspending() { /* Given */ val m = mock { - onBlocking { intResult() } doReturn 42 + on { intResult() } doReturn 42 } /* When */ @@ -46,7 +45,7 @@ class CoroutinesTest { fun stubbingSuspending_usingSuspendingFunction() { /* Given */ val m = mock { - onBlocking { intResult() } doReturn runBlocking { Open().intResult(42) } + on { intResult() } doReturn runBlocking { Open().intResult(42) } } /* When */ @@ -60,7 +59,7 @@ class CoroutinesTest { fun stubbingSuspending_runBlocking() = runBlocking { /* Given */ val mock = mock { - onBlocking { intResult() } doReturn 42 + on { intResult() } doReturn 42 } /* When */ @@ -74,7 +73,7 @@ class CoroutinesTest { fun stubbingSuspending_wheneverBlocking() { /* Given */ val mock: SuspendFunctions = mock() - wheneverBlocking { mock.intResult() } + whenever { mock.intResult() } .doReturn(42) /* When */ @@ -88,10 +87,7 @@ class CoroutinesTest { fun stubbingSuspending_doReturn() { /* Given */ val spy = spy(Open()) - doReturn(10) - .wheneverBlocking(spy) { - delayedIntResult() - } + doReturn(10).whenever(spy) { delayedIntResult() } /* When */ val result = runBlocking { spy.delayedIntResult() } @@ -104,7 +100,7 @@ class CoroutinesTest { fun stubbingNonSuspending() { /* Given */ val mock = mock { - onBlocking { intResult() } doReturn 42 + on { intResult() } doReturn 42 } /* When */ @@ -118,7 +114,7 @@ class CoroutinesTest { fun stubbingNonSuspending_runBlocking() = runBlocking { /* Given */ val mock = mock { - onBlocking { intResult() } doReturn 42 + on { intResult() } doReturn 42 } /* When */ @@ -207,7 +203,7 @@ class CoroutinesTest { fun answerWithSuspendFunction() = runBlocking { val mock: SuspendFunctions = mock() - whenever(mock.intResult(any())).doSuspendableAnswer { + whenever { mock.intResult(any()) } doSuspendableAnswer { withContext(Dispatchers.Default) { it.getArgument(0) } } @@ -217,7 +213,7 @@ class CoroutinesTest { @Test fun inplaceAnswerWithSuspendFunction() = runBlocking { val mock: SuspendFunctions = mock { - onBlocking { intResult(any()) } doSuspendableAnswer { + on { intResult(any()) } doSuspendableAnswer { withContext(Dispatchers.Default) { it.getArgument(0) } } } @@ -229,7 +225,7 @@ class CoroutinesTest { fun callFromSuspendFunction() = runBlocking { val mock: SuspendFunctions = mock() - whenever(mock.intResult(any())).doSuspendableAnswer { + whenever { mock.intResult(any()) } doSuspendableAnswer { withContext(Dispatchers.Default) { it.getArgument(0) } } @@ -247,7 +243,7 @@ class CoroutinesTest { fun callFromActor() = runBlocking { val mock: SuspendFunctions = mock() - whenever(mock.intResult(any())).doSuspendableAnswer { + whenever { mock.intResult(any()) } doSuspendableAnswer { withContext(Dispatchers.Default) { it.getArgument(0) } } @@ -269,7 +265,7 @@ class CoroutinesTest { fun answerWithSuspendFunctionWithoutArgs() = runBlocking { val mock: SuspendFunctions = mock() - whenever(mock.intResult()).doSuspendableAnswer { + whenever { mock.intResult() } doSuspendableAnswer { withContext(Dispatchers.Default) { 42 } } @@ -280,7 +276,7 @@ class CoroutinesTest { fun answerWithSuspendFunctionWithDestructuredArgs() = runBlocking { val mock: SuspendFunctions = mock() - whenever(mock.intResult(any())).doSuspendableAnswer { (i: Int) -> + whenever { mock.intResult(any()) } doSuspendableAnswer { (i: Int) -> withContext(Dispatchers.Default) { i } } @@ -293,7 +289,7 @@ class CoroutinesTest { val job = Job() - whenever(mock.intResult()).doSuspendableAnswer { + whenever { mock.intResult() } doSuspendableAnswer { job.join() 5 } diff --git a/tests/src/test/kotlin/test/MatchersTest.kt b/tests/src/test/kotlin/test/MatchersTest.kt index e8e3c625..393eafdb 100644 --- a/tests/src/test/kotlin/test/MatchersTest.kt +++ b/tests/src/test/kotlin/test/MatchersTest.kt @@ -246,7 +246,7 @@ class MatchersTest : TestBase() { @Test fun anyVarargMatching() { mock().apply { - whenever(varargBooleanResult(anyVararg())).thenReturn(true) + whenever { varargBooleanResult(anyVararg()) }.thenReturn(true) expect(varargBooleanResult()).toBe(true) } } @@ -796,7 +796,7 @@ class MatchersTest : TestBase() { val matcher = VarargAnyMatcher({ "b" == it }, String::class.java, true, false) /* When */ - whenever(t.varargBooleanResult(argThat(matcher))).thenAnswer(matcher) + whenever { t.varargBooleanResult(argThat(matcher)) }.thenAnswer(matcher) /* Then */ expect(t.varargBooleanResult("a", "b", "c")).toBe(true) @@ -810,7 +810,7 @@ class MatchersTest : TestBase() { val matcher = VarargAnyMatcher({ "d" == it }, String::class.java, true, false) /* When */ - whenever(t.varargBooleanResult(argThat(matcher))).thenAnswer(matcher) + whenever { t.varargBooleanResult(argThat(matcher)) }.thenAnswer(matcher) /* Then */ expect(t.varargBooleanResult("a", "b", "c")).toBe(false) diff --git a/tests/src/test/kotlin/test/MockingTest.kt b/tests/src/test/kotlin/test/MockingTest.kt index 08c61662..5f5192ad 100644 --- a/tests/src/test/kotlin/test/MockingTest.kt +++ b/tests/src/test/kotlin/test/MockingTest.kt @@ -78,7 +78,7 @@ class MockingTest : TestBase() { @Test fun deepStubs() { val cal: Calendar = mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS) - whenever(cal.time.time).thenReturn(123L) + whenever { cal.time.time }.thenReturn(123L) expect(cal.time.time).toBe(123L) } @@ -99,10 +99,10 @@ class MockingTest : TestBase() { @Test fun testMockStubbing_normalOverridesLambda() { /* Given */ - val mock = mock() { - on { stringResult() }.doReturn("A") + val mock = mock { + on { stringResult() } doReturn "A" } - whenever(mock.stringResult()).thenReturn("B") + whenever { mock.stringResult() }.thenReturn("B") /* When */ val result = mock.stringResult() @@ -325,7 +325,7 @@ class MockingTest : TestBase() { fun mockSuspendFunction_withClosedBooleanReturn_name() = runTest { /* Given */ val mock = mock(name = "myName") { - onBlocking { closedBooleanResult(any()) } doReturn true + on { closedBooleanResult(any()) } doReturn true } /* When */ @@ -339,7 +339,7 @@ class MockingTest : TestBase() { fun mockSuspendFunction_withClassClosedBooleanReturn_name() = runTest { /* Given */ val mock = mock(name = "myName") { - onBlocking { classClosedBooleanResult(any()) } doReturn true + on { classClosedBooleanResult(any()) } doReturn true } /* When */ @@ -464,7 +464,7 @@ class MockingTest : TestBase() { @Test fun mockConstruction_withInitializer() { mockConstruction { mock, _ -> - whenever(mock.stringResult()).thenReturn("Hello") + whenever { mock.stringResult() }.thenReturn("Hello") }.use { val open = Open() diff --git a/tests/src/test/kotlin/test/OngoingStubbingTest.kt b/tests/src/test/kotlin/test/OngoingStubbingTest.kt index 0e980f4f..f6c8b57c 100644 --- a/tests/src/test/kotlin/test/OngoingStubbingTest.kt +++ b/tests/src/test/kotlin/test/OngoingStubbingTest.kt @@ -2,6 +2,7 @@ package test import com.nhaarman.expect.expect import com.nhaarman.expect.expectErrorWithMessage +import kotlinx.coroutines.runBlocking import org.junit.Assume.assumeFalse import org.junit.Test import org.mockito.Mockito @@ -125,36 +126,48 @@ class OngoingStubbingTest : TestBase() { assertThrows { mock.builderMethod() } + // any consecutive call should throw the last specified exception + assertThrows { + mock.builderMethod() + } } @Test - fun `should throw exception class on function call`() { + fun `should throw exception instances on consecutive function calls`() { /* Given */ val mock = mock { - on { builderMethod() } doThrow IllegalArgumentException::class + on { builderMethod() }.doThrow( + IllegalArgumentException(), + UnsupportedOperationException() + ) } /* When, Then */ assertThrows { mock.builderMethod() } + assertThrows { + mock.builderMethod() + } + // any consecutive call should throw the last specified exception + assertThrows { + mock.builderMethod() + } } @Test - fun `should throw exception instances on consecutive function calls`() { + fun `should throw exception class on function call`() { /* Given */ val mock = mock { - on { builderMethod() }.doThrow( - IllegalArgumentException(), - UnsupportedOperationException() - ) + on { builderMethod() } doThrow IllegalArgumentException::class } /* When, Then */ assertThrows { mock.builderMethod() } - assertThrows { + // any consecutive call should throw the last specified exception + assertThrows { mock.builderMethod() } } @@ -176,6 +189,10 @@ class OngoingStubbingTest : TestBase() { assertThrows { mock.builderMethod() } + // any consecutive call should throw the last specified exception + assertThrows { + mock.builderMethod() + } } @Test @@ -252,10 +269,10 @@ class OngoingStubbingTest : TestBase() { } @Test - fun `should stub function call with integer result`() { + fun `should stub generics function call with explicit generics type`() { /* Given */ val mock = mock> { - onGeneric { genericMethod() } doReturn 2 + onGeneric({ genericMethod() }, Int::class) doReturn 2 } /* Then */ @@ -263,12 +280,31 @@ class OngoingStubbingTest : TestBase() { } @Test - fun `should stub nullable function call with string result`() { - val m = mock> { - onGeneric { nullableReturnType() } doReturn "Test" + fun `should stub nullable generics function call with string result`() { + /* Given */ + val mock = mock> { + on { nullableReturnType() } doReturn "Test" } - expect(m.nullableReturnType()).toBe("Test") + /* When */ + val result = mock.nullableReturnType() + + /* Then */ + expect(result).toBe("Test") + } + + @Test + fun `should stub suspendable generics function call with integer result`() { + /* Given */ + val mock = mock> { + on { suspendableGenericMethod() } doReturn 2 + } + + /* When */ + val result = runBlocking { mock.suspendableGenericMethod() } + + /* Then */ + expect(result).toBe(2) } @Test @@ -353,7 +389,7 @@ class OngoingStubbingTest : TestBase() { val valueClassA = ValueClass("A") val valueClassB = ValueClass("B") val mock = mock { - on { valueClassResult() }.doReturnConsecutively(listOf(valueClassA, valueClassB)) + on { valueClassResult() } doReturnConsecutively listOf(valueClassA, valueClassB) } /* When */ diff --git a/tests/src/test/kotlin/test/StubberTest.kt b/tests/src/test/kotlin/test/StubberTest.kt index 5144cb7e..0c7a4c87 100644 --- a/tests/src/test/kotlin/test/StubberTest.kt +++ b/tests/src/test/kotlin/test/StubberTest.kt @@ -1,14 +1,23 @@ package test import com.nhaarman.expect.expect -import com.nhaarman.expect.expectErrorWithMessage +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals import org.junit.Test -import org.mockito.kotlin.* +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doCallRealMethod +import org.mockito.kotlin.doNothing +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.doThrow +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.whenever class StubberTest : TestBase() { @Test - fun testDoAnswer() { + fun `should stub function call with result from lambda`() { val mock = mock() doAnswer { "Test" } @@ -19,7 +28,7 @@ class StubberTest : TestBase() { } @Test - fun testDoCallRealMethod() { + fun `should stub function call to call real method implementation`() { val mock = mock() doReturn("Test").whenever(mock).stringResult() @@ -29,7 +38,7 @@ class StubberTest : TestBase() { } @Test - fun testDoNothing() { + fun `should stub function call to do nothing in the spied instance`() { val spy = spy(Open()) val array = intArrayOf(3) @@ -40,7 +49,7 @@ class StubberTest : TestBase() { } @Test - fun testDoReturnValue() { + fun `should stub function call with fixed return value`() { val mock = mock() doReturn("test").whenever(mock).stringResult() @@ -49,7 +58,17 @@ class StubberTest : TestBase() { } @Test - fun testDoReturnNullValue() { + fun `should stub function call with consecutive fixed return values`() { + val mock = mock() + + doReturn("test", "test2").whenever(mock).stringResult() + + expect(mock.stringResult()).toBe("test") + expect(mock.stringResult()).toBe("test2") + } + + @Test + fun `should stub function call with null return value`() { val mock = mock() doReturn(null).whenever(mock).stringResult() @@ -58,7 +77,7 @@ class StubberTest : TestBase() { } @Test - fun testDoReturnNullValues() { + fun `should stub function call with consecutive null return values`() { val mock = mock() doReturn(null, null).whenever(mock).stringResult() @@ -68,41 +87,96 @@ class StubberTest : TestBase() { } @Test - fun testDoReturnValues() { - val mock = mock() + fun `should stub function call to throw exception instance`() { + val mock = mock() - doReturn("test", "test2").whenever(mock).stringResult() + doThrow(IllegalStateException("test")).whenever(mock).go() - expect(mock.stringResult()).toBe("test") - expect(mock.stringResult()).toBe("test2") + val exception: IllegalStateException = assertThrows { + mock.go() + } + assertEquals("test", exception.message) + // any consecutive call should throw the last specified exception + assertThrows { + mock.go() + } + } + + @Test + fun `should stub function call to throw exception instances consecutively`() { + val mock = mock() + + doThrow( + IllegalStateException("test"), + NullPointerException("test2") + ).whenever(mock).go() + + val first: IllegalStateException = assertThrows { + mock.go() + } + assertEquals("test", first.message) + val second: NullPointerException = assertThrows { + mock.go() + } + assertEquals("test2", second.message) + // any consecutive call should throw the last specified exception + assertThrows { + mock.go() + } } @Test - fun testDoThrowClass() { + fun `should stub function call to throw exception class`() { val mock = mock() doThrow(IllegalStateException::class).whenever(mock).go() - try { + assertThrows { + mock.go() + } + // any consecutive call should throw the last specified exception + assertThrows { mock.go() - throw AssertionError("Call should have thrown.") - } catch (_: IllegalStateException) { } } @Test - fun testDoThrow() { + fun `should stub function call to throw exception classes consecutively`() { val mock = mock() - doThrow(IllegalStateException("test")).whenever(mock).go() + doThrow( + IllegalStateException::class, + NullPointerException::class + ).whenever(mock).go() - expectErrorWithMessage("test").on { + assertThrows { + mock.go() + } + assertThrows { mock.go() } + // any consecutive call should throw the last specified exception + assertThrows { + mock.go() + } + } + + @Test + fun `should stub suspendable function call in reverse manner, with on() as part of mock creation`() = runTest{ + /* Given */ + val mock = mock { + doReturn( "A") on { stringResult() } + } + + /* When */ + val result = runBlocking { mock.stringResult() } + + /* Then */ + expect(result).toBe("A") } @Test - fun testStubberOnBlockExtension() { + fun `should stub synchronous function call in reverse manner, with on() as part of mock creation`() = runTest{ val mock = mock { doReturn("Test").on { stringResult() } } diff --git a/tests/src/test/kotlin/test/StubbingTest.kt b/tests/src/test/kotlin/test/StubbingTest.kt index 77a1c386..77ec5ebf 100644 --- a/tests/src/test/kotlin/test/StubbingTest.kt +++ b/tests/src/test/kotlin/test/StubbingTest.kt @@ -64,6 +64,18 @@ class StubbingTest { expect(result).toBe("result") } + @Test + fun `should stub already existing mock, using whenever function`() { + val mock = mock() + whenever { mock.stringResult() } doReturn "result" + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("result") + } + @Test fun `should override default stub of mock`() { /* Given mock with stub */ @@ -135,7 +147,7 @@ class StubbingTest { fun `should throw when stubbing is incomplete`() { /* Given */ val mock = mock() - whenever(mock.stringResult()) + whenever { mock.stringResult() } /* When */ try { @@ -143,14 +155,13 @@ class StubbingTest { } catch (e: UnfinishedStubbingException) { /* Then */ expect(e.message).toContain("Unfinished stubbing detected here:") - expect(e.message).toContain("-> at test.StubbingTest.should throw when stubbing is incomplete") } } @Test fun `should stub sync method call in reverse manner as part of mock creation`() { val mock = mock { - doReturn("A").on { stringResult() } + doReturn("A") on { stringResult() } } expect(mock.stringResult()).toBe("A") @@ -160,7 +171,7 @@ class StubbingTest { fun `should stub sync method call, with whenever`() { /* Given */ val mock = mock() - whenever(mock.stringResult()).doReturn("A") + whenever { mock.stringResult() } doReturn "A" /* When */ val result = mock.stringResult() @@ -213,10 +224,10 @@ class StubbingTest { } @Test - fun `should stub suspendable function call, with wheneverBlocking as part of mock creation`() { + fun `should stub suspendable function call, with whenever as part of mock creation`() { /* Given */ val mock = mock { mock -> - wheneverBlocking { mock.stringResult() } doReturn "A" + whenever { mock.stringResult() } doReturn "A" } /* When */ @@ -227,10 +238,24 @@ class StubbingTest { } @Test - fun `should stub suspendable function call in reverse manner, with wheneverBlocking as part of mock creation`() { + fun `should stub sync method call in reverse manner, with whenever() and lambda as part of mock creation`() { /* Given */ - val mock = mock { mock -> - doReturn( "A").wheneverBlocking(mock) { mock.stringResult() } + val mock = mock { mock -> + doReturn( "A").whenever(mock) { stringResult() } + } + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should stub suspendable function call in reverse manner with on() as part of mock creation`() { + /* Given */ + val mock = mock { + doReturn( "A") on { stringResult() } } /* When */ @@ -241,9 +266,23 @@ class StubbingTest { } @Test - fun `should stub suspendable function call in reverse manner, with whenever as part of mock creation`() = runTest{ + fun `should stub suspendable function call in reverse manner, with whenever() and lambda as part of mock creation`() { /* Given */ - val mock = mock { mock -> + val mock = mock { mock -> + doReturn( "A").whenever(mock) { stringResult() } + } + + /* When */ + val result = runBlocking { mock.stringResult() } + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should stub suspendable function call in reverse manner, with whenever() as part of mock creation`() = runTest{ + /* Given */ + val mock = mock { doReturn( "A").whenever(mock).stringResult() } @@ -258,7 +297,7 @@ class StubbingTest { fun `should stub sync function call within a suspendable lambda`() { /* Given */ val mock = mock { - onBlocking { stringResult() } doReturn "A" + on { stringResult() } doReturn "A" } /* When */ @@ -269,10 +308,10 @@ class StubbingTest { } @Test - fun `should stub sync function call within a suspendable lambda of wheneverBlocking`() { + fun `should stub sync function call within a suspendable lambda of whenever`() { /* Given */ val mock = mock() - wheneverBlocking { mock.stringResult() } doReturn "A" + whenever { mock.stringResult() } doReturn "A" /* When */ val result = mock.stringResult() @@ -282,7 +321,7 @@ class StubbingTest { } @Test - fun `should stub suspend function call with a call to the sync version of whenever`() = runTest { + fun `should stub suspendable function call with a call to the sync version of whenever`() = runTest { /* Given */ val mock = mock() whenever (mock.stringResult()) doReturn "A" @@ -295,10 +334,10 @@ class StubbingTest { } @Test - fun `should provide backwards support to stub suspendable function call with 'whenever' method`() = runTest { + fun `should support to stub suspendable function call with synchronous 'whenever' method`() = runTest { /* Given */ val mock = mock() - whenever(mock.stringResult()).doSuspendableAnswer { + whenever(mock.stringResult()) doSuspendableAnswer { delay(0) "A" } @@ -309,4 +348,63 @@ class StubbingTest { /* Then */ expect(result).toBe("A") } + + @Test + @Suppress("DEPRECATION") + fun `should provide backwards compatibility support to stub suspendable function call with 'wheneverBlocking' method`() { + /* Given */ + val mock = mock() + wheneverBlocking { mock.stringResult() } doReturn "A" + + /* When */ + val result = runBlocking { mock.stringResult() } + + /* Then */ + expect(result).toBe("A") + } + + @Test + @Suppress("DEPRECATION") + fun `should provide backwards compatibility support to stub suspendable function call in reverse manner, with wheneverBlocking`() { + /* Given */ + val mock = mock { mock -> + doReturn( "A").wheneverBlocking(mock) { stringResult() } + } + + /* When */ + val result = runBlocking { mock.stringResult() } + + /* Then */ + expect(result).toBe("A") + } + + @Test + @Suppress("DEPRECATION") + fun `should provide backwards compatibility support to stub suspendable function call with 'onBlocking' method`() { + /* Given */ + val mock = mock { + onBlocking { mock.stringResult() } doReturn "A" + } + + /* When */ + val result = runBlocking { mock.stringResult() } + + /* Then */ + expect(result).toBe("A") + } + + @Test + @Suppress("DEPRECATION") + fun `should provide backwards compatibility support to stub generics function calls with integer result`() { + /* Given */ + val mock = mock> { + onGeneric { genericMethod() } doReturn 2 + } + + /* When */ + val result = mock.genericMethod() + + /* Then */ + expect(result).toBe(2) + } } diff --git a/tests/src/test/kotlin/test/inline/UsingMockMakerInlineTest.kt b/tests/src/test/kotlin/test/inline/UsingMockMakerInlineTest.kt index b03d77ea..d6bb6192 100644 --- a/tests/src/test/kotlin/test/inline/UsingMockMakerInlineTest.kt +++ b/tests/src/test/kotlin/test/inline/UsingMockMakerInlineTest.kt @@ -74,7 +74,7 @@ class UsingMockMakerInlineTest { fun mockClosedFunction_mockStubbing() { /* Given */ val mock = mock { - on { doSomethingElse(any()) } doReturn (BigInteger.ONE) + on { doSomethingElse(any()) } doReturn BigInteger.ONE } /* When */ @@ -88,7 +88,7 @@ class UsingMockMakerInlineTest { fun mockClosedFunction_whenever() { /* Given */ val mock = mock() - whenever(mock.doSomethingElse(any())).doReturn(BigInteger.ONE) + whenever { mock.doSomethingElse(any()) } doReturn BigInteger.ONE /* When */ val result = mock.doSomethingElse(BigInteger.TEN) From 53e5146dcba7191e07b0d45f46c3952ff2d16ab3 Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Sun, 7 Dec 2025 21:40:37 +0100 Subject: [PATCH 2/6] Improve unit tests for stubbing value class return values of suspendable functions. --- .../test/CoroutinesOngoingStubbingTest.kt | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt b/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt index fc089057..ee2cf798 100644 --- a/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt +++ b/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt @@ -9,6 +9,7 @@ import org.junit.Test import org.mockito.Mockito import org.mockito.kotlin.any import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doCallRealMethod import org.mockito.kotlin.doReturn import org.mockito.kotlin.doReturnConsecutively import org.mockito.kotlin.doSuspendableAnswer @@ -288,7 +289,8 @@ class CoroutinesOngoingStubbingTest { /* Given */ val valueClass = ValueClass("A") val mock = mock { - on(mock.valueClassResult()) doSuspendableAnswer { + // TODO: simplify to "on (mock.valueClassResult()) doReturn valueClass" + on (mock.valueClassResult()) doSuspendableAnswer { delay(1) valueClass } @@ -302,36 +304,38 @@ class CoroutinesOngoingStubbingTest { } @Test - fun `should stub suspendable function call with nullable value class result`() = runTest { + fun `should stub suspendable function call with nullable value class result`() { /* Given */ val valueClass = ValueClass("A") val mock = mock { - on (mock.nullableValueClassResult()) doSuspendableAnswer { + // TODO: simplify to "on { nullableValueClassResult() } doReturn valueClass" + on { nullableValueClassResult() } doSuspendableAnswer { delay(1) valueClass } } /* When */ - val result: ValueClass? = mock.nullableValueClassResult() + val result: ValueClass? = runBlocking { mock.nullableValueClassResult() } /* Then */ expect(result).toBe(valueClass) } @Test - fun `should stub suspendable function call with nested value class result`() = runTest { + fun `should stub suspendable function call with nested value class result`() { /* Given */ val nestedValueClass = NestedValueClass(ValueClass("A")) val mock = mock { - on (mock.nestedValueClassResult()) doSuspendableAnswer { + // TODO: simplify to "on { nestedValueClassResult() } doReturn nestedValueClass" + on { nestedValueClassResult() } doSuspendableAnswer { delay(1) nestedValueClass } } /* When */ - val result: NestedValueClass = mock.nestedValueClassResult() + val result: NestedValueClass = runBlocking { mock.nestedValueClassResult() } /* Then */ expect(result).toBe(nestedValueClass) @@ -373,4 +377,30 @@ class CoroutinesOngoingStubbingTest { /* Then */ expect(result).toBe(primitiveValueClass) } + + @Test + fun `should stub consecutive suspendable function call with value class results`() { + /* Given */ + val valueClassA = ValueClass("A") + val valueClassB = ValueClass("B") + val mock = mock { + // TODO: simplify to "on { valueClassResult() }.doReturnConsecutively(valueClassA, valueClassB)" + on { valueClassResult() } doSuspendableAnswer { + delay(1) + valueClassA + } doSuspendableAnswer { + delay(1) + valueClassB + } + } + + /* When */ + val (result1, result2) = runBlocking { + mock.valueClassResult() to mock.valueClassResult() + } + + /* Then */ + expect(result1).toBe(valueClassA) + expect(result2).toBe(valueClassB) + } } From d3240af13b9496c63bc8d5c97bebc418fc2bd4ad Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Mon, 15 Dec 2025 22:01:14 +0100 Subject: [PATCH 3/6] Adjust documentation on Matcher functions --- .../kotlin/org/mockito/kotlin/Matchers.kt | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Matchers.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Matchers.kt index 80f4b022..271ae180 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Matchers.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Matchers.kt @@ -35,7 +35,7 @@ import org.mockito.kotlin.internal.unboxValueClass import org.mockito.kotlin.internal.valueClassInnerClass import kotlin.reflect.KClass -/** Object argument that is equal to the given value. */ +/** Matches an argument that is equal to the given value. */ inline fun eq(value: T): T { if (T::class.isValue) return eqValueClass(value) @@ -43,7 +43,7 @@ inline fun eq(value: T): T { return ArgumentMatchers.eq(value) ?: value } -/** Object argument that is the same as the given value. */ +/** Matches an argument that is the same as the given value. */ fun same(value: T): T { return ArgumentMatchers.same(value) ?: value } @@ -61,7 +61,7 @@ inline fun anyOrNull(): T { return ArgumentMatchers.any() ?: createInstance() } -/** Matches any vararg object, including nulls. */ +/** Matches any vararg argument, including nulls. */ inline fun anyVararg(): T { return anyVararg(T::class) } @@ -88,6 +88,7 @@ inline fun anyValueClass(): T { return ArgumentMatchers.any(clazz.valueClassInnerClass().java).boxAsValueClass(clazz) } +/** Matches an argument that is equal to the given Kotlin value class value. */ inline fun eqValueClass(value: T): T { require(value::class.isValue) { "${value::class.qualifiedName} is not a value class." } @@ -101,7 +102,7 @@ inline fun eqValueClass(value: T): T { } /** - * Creates a custom argument matcher. + * Matches an argument that is matching the given predicate. * `null` values will never evaluate to `true`. * * @param predicate An extension function on [T] that returns `true` when a [T] matches the predicate. @@ -113,6 +114,8 @@ inline fun argThat(noinline predicate: T.() -> Boolean): T { } /** + * Matches an argument that is matching the given [ArgumentMatcher]. + * * Registers a custom ArgumentMatcher. The original Mockito function registers the matcher and returns null, * here the required type is returned. * @@ -123,6 +126,8 @@ inline fun argThat(matcher: ArgumentMatcher): T { } /** + * Matches an argument that is matching the given [ArgumentMatcher]. + * * Alias for [argThat]. * * Creates a custom argument matcher. @@ -135,7 +140,7 @@ inline fun argForWhich(noinline predicate: T.() -> Boolean): T } /** - * Creates a custom argument matcher. + * Matches an argument that is matching the given predicate. * `null` values will never evaluate to `true`. * * @param predicate A function that returns `true` when given [T] matches the predicate. @@ -145,33 +150,33 @@ inline fun argWhere(noinline predicate: (T) -> Boolean): T { } /** - * Argument that implements the given class. + * Matches an argument that is instance of the given class. */ inline fun isA(): T { return ArgumentMatchers.isA(T::class.java) ?: createInstance() } /** - * `null` argument. + * Matches an argument that is `null`. */ fun isNull(): T? = ArgumentMatchers.isNull() /** - * Not `null` argument. + * Matches an argument that is not `null`. */ fun isNotNull(): T? { return ArgumentMatchers.isNotNull() } /** - * Not `null` argument. + * Matches an argument that is not `null`. */ fun notNull(): T? { return ArgumentMatchers.notNull() } /** - * Object argument that is reflection-equal to the given value with support for excluding + * Matches an argument that is reflection-equal to the given value with support for excluding * selected fields from a class. */ inline fun refEq(value: T, vararg excludeFields: String): T { From 51d68d4186a7e84b4ff6f6b665298e0242a2070d Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Wed, 17 Dec 2025 21:15:20 +0100 Subject: [PATCH 4/6] Revert unnecessary additions of inline/reified nature to functions --- .../src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt index 7851bf34..996c4a65 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt @@ -63,7 +63,7 @@ import kotlin.reflect.KClass * @return OngoingStubbing object used to stub fluently. * ***Do not*** create a reference to this returned object. */ -inline fun whenever(methodCall: T): OngoingStubbing { +fun whenever(methodCall: T): OngoingStubbing { return `when`(methodCall)!! } @@ -137,7 +137,7 @@ fun wheneverBlocking(methodCall: suspend CoroutineScope.() -> T): OngoingStu * @return OngoingStubbing object used to stub fluently. * ***Do not*** create a reference to this returned object. */ -inline infix fun OngoingStubbing.doReturn(value: T): OngoingStubbing { +infix fun OngoingStubbing.doReturn(value: T): OngoingStubbing { return thenReturn(value) } From 6a5170ee8de9a77a5429cc1399af648a129f917f Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Wed, 17 Dec 2025 22:29:45 +0100 Subject: [PATCH 5/6] Reducing wordy KDoc documentation --- .../kotlin/org/mockito/kotlin/KStubbing.kt | 66 +++------- .../org/mockito/kotlin/OngoingStubbing.kt | 106 ++++++--------- .../main/kotlin/org/mockito/kotlin/Stubber.kt | 124 +++++------------- 3 files changed, 91 insertions(+), 205 deletions(-) diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KStubbing.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KStubbing.kt index b7c7c7d5..bca928de 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KStubbing.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KStubbing.kt @@ -63,9 +63,8 @@ class KStubbing(val mock: T) { } /** - * Enables stubbing java methods or kotlin functions. Use it when you want the mock to return - * a particular value when particular method/function is being called. - * The kotlin function call to be stubbed can be either a synchronous or suspendable function. + * Enables stubbing methods/function calls. + * In case of Kotlin function calls, these can be either synchronous or suspendable function calls. * * Simply put: "**on a call to** the x function **then** return y". * @@ -77,12 +76,8 @@ class KStubbing(val mock: T) { * } * ``` * - * This function acts as an alias for [Mockito.when], as `when` is a keyword in kotlin and as such - * the Mockito method can only be called by wrapping the method name in backticks, e.g. `` `when` ``. - * To reduce the noise of backticks in your code, you can use this the function [whenever] instead. - * - * For more detail documentation, please refer to the Javadoc in the [Mockito] class. - * + * This function is an alias for [Mockito.when]. So, for more detailed documentation, + * please refer to the Javadoc of that method in the [Mockito] class. * For stubbing Unit functions (or Java void methods) with throwables, see: [Mockito.doThrow]. * * @param methodCall method call to be stubbed. @@ -92,9 +87,8 @@ class KStubbing(val mock: T) { inline fun on(methodCall: R): OngoingStubbing = whenever(methodCall) /** - * Enables stubbing java methods or kotlin functions. Use it when you want the mock to return - * a particular value when particular method/function is being called. - * The kotlin function call to be stubbed can be either a synchronous or suspendable function. + * Enables stubbing methods/function calls. + * In case of Kotlin function calls, these can be either synchronous or suspendable function calls. * * Simply put: "**on a call to** the x function **then** return y". * @@ -106,12 +100,8 @@ class KStubbing(val mock: T) { * } * ``` * - * This function acts as an alias for [Mockito.when], as `when` is a keyword in kotlin and as such - * the Mockito method can only be called by wrapping the method name in backticks, e.g. `` `when` ``. - * To reduce the noise of backticks in your code, you can use this the function [whenever] instead. - * - * For more detail documentation, please refer to the Javadoc in the [Mockito] class. - * + * This function is an alias for [Mockito.when]. So, for more detailed documentation, + * please refer to the Javadoc of that method in the [Mockito] class. * For stubbing Unit functions (or Java void methods) with throwables, see: [Mockito.doThrow]. * * @param methodCall method call to be stubbed. @@ -130,10 +120,8 @@ class KStubbing(val mock: T) { } /** - * Enables stubbing java methods or kotlin functions with a generics return type [R]. - * Use it when you want the mock to return a particular value when particular method/function - * is being called. - * The kotlin function call to be stubbed can be either a synchronous or suspendable function. + * Enables stubbing methods/function calls with a generics return type [R]. + * In case of Kotlin function calls, these can be either synchronous or suspendable function calls. * * Simply put: "**on a call to** the x function **then** return y". * @@ -149,12 +137,8 @@ class KStubbing(val mock: T) { * } * ``` * - * This function acts as an alias for [Mockito.when], as `when` is a keyword in kotlin and as such - * the Mockito method can only be called by wrapping the method name in backticks, e.g. `` `when` ``. - * To reduce the noise of backticks in your code, you can use this the function [whenever] instead. - * - * For more detail documentation, please refer to the Javadoc in the [Mockito] class. - * + * This function is an alias for [Mockito.when]. So, for more detailed documentation, + * please refer to the Javadoc of that method in the [Mockito] class. * For stubbing Unit functions (or Java void methods) with throwables, see: [Mockito.doThrow]. * * @param methodCall method call to be stubbed. @@ -178,10 +162,8 @@ class KStubbing(val mock: T) { } /** - * Enables stubbing java methods or kotlin functions with a generics return type [R]. - * Use it when you want the mock to return a particular value when particular method/function - * is being called. - * The kotlin function call to be stubbed can be either a synchronous or suspendable function. + * Enables stubbing methods/function calls with a generics return type [R]. + * In case of Kotlin function calls, these can be either synchronous or suspendable function calls. * * Simply put: "**on a call to** the x function **then** return y". * @@ -199,8 +181,6 @@ class KStubbing(val mock: T) { * * This is a deprecated alias for [on]. Please use [on] instead. * - * For more detailed documentation, please refer to [on]. - * * @param methodCall method call to be stubbed. * @return OngoingStubbing object used to stub fluently. * ***Do not*** create a reference to this returned object. @@ -211,9 +191,8 @@ class KStubbing(val mock: T) { } /** - * Enables stubbing a (suspendable) kotlin functions. Use it when you want the mock to return - * a particular value when particular function is being called. - * The kotlin function call to be stubbed can be either a synchronous or suspendable function. + * Enables stubbing methods/function calls. + * In case of Kotlin function calls, these can be either synchronous or suspendable function calls. * * Simply put: "**on a call to** the x function **then** return y". * @@ -225,14 +204,6 @@ class KStubbing(val mock: T) { * } * ``` * - * This function acts as an alias for [Mockito.when], as `when` is a keyword in kotlin and as such - * the Mockito method can only be called by wrapping the method name in backticks, e.g. `` `when` ``. - * To reduce the noise of backticks in your code, you can use this function [on] instead. - * - * For more detail documentation, please refer to the Javadoc in the [Mockito] class. - * - * For stubbing Unit functions (or Java void methods) with throwables, see: [Mockito.doThrow]. - * * This is a deprecated alias for [on]. Please use [on] instead. * * @param methodCall (regular or suspendable) lambda, wrapping the function call to be stubbed. @@ -245,7 +216,7 @@ class KStubbing(val mock: T) { } /** - * Sets the method call to be stubbed in a reverse manner, as part of a mock being created. + * Stubs a method/function call in a reverse manner, as part of a mock being created. * You can reverse stub either synchronous as well as suspendable function calls. * * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as @@ -261,8 +232,7 @@ class KStubbing(val mock: T) { * ``` * Warning: Only one method call can be stubbed in the function. Subsequent method calls are ignored! * - * This function acts as an alias for [whenever]. - * For more detailed documentation, please refer to [whenever]. + * This function is an alias for [whenever]. Please use [whenever] instead. * * @param methodCall (regular or suspendable) lambda, wrapping the method/function call to be stubbed. */ diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt index 996c4a65..3c5ee76c 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt @@ -36,9 +36,8 @@ import org.mockito.stubbing.OngoingStubbing import kotlin.reflect.KClass /** - * Enables stubbing java methods or kotlin functions. Use it when you want the mock to return - * a particular value when particular method/function is being called. - * The kotlin function call to be stubbed can be either a synchronous or suspendable function. + * Enables stubbing methods/function calls. + * In case of Kotlin function calls, these can be either synchronous or suspendable function calls. * * Simply put: "**Whenever** the x function is being called **then** return y". * @@ -51,12 +50,8 @@ import kotlin.reflect.KClass * whenever(mock.someFunction(anyString())) doReturn 10 * ``` * - * This function acts as an alias for [Mockito.when], as `when` is a keyword in kotlin and as such - * the Mockito method can only be called by wrapping the method name in backticks, e.g. `` `when` ``. - * To reduce the noise of backticks in your code, you can use this the function [whenever] instead. - * - * For more detail documentation, please refer to the Javadoc in the [Mockito] class. - * + * This function is an alias for [Mockito.when]. So, for more detailed documentation, + * please refer to the Javadoc of that method in the [Mockito] class. * For stubbing Unit functions (or Java void methods) with throwables, see: [Mockito.doThrow]. * * @param methodCall method call to be stubbed. @@ -68,10 +63,8 @@ fun whenever(methodCall: T): OngoingStubbing { } /** - * Enables stubbing java methods or kotlin functions. Use it when you want the mock to return - * a particular value when particular method/function is being called. - * The lambda with the method/function call to be stubbed can be either a synchronous (regular) - * or suspend lambda. + * Enables stubbing methods/function calls. + * In case of Kotlin function calls, these can be either synchronous or suspendable function calls. * * **Warning**: Only the first method/function call in the lambda will be stubbed, other methods/functions calls are ignored! * @@ -86,15 +79,9 @@ fun whenever(methodCall: T): OngoingStubbing { * whenever { mock.someFunction(anyString()) } doReturn 10 * ``` * - * This function acts as an alias for [Mockito.when], as `when` is a keyword in kotlin and as such - * the Mockito method can only be called by wrapping the method name in backticks, e.g. `` `when` ``. - * To reduce the noise of backticks in your code, you can use this the function [whenever] instead. - * Next to that, this function will take care of handling with suspend lambda, to ease the stubbing - * of a suspendable function call. - * - * For more detailed documentation, please refer to the Javadoc in the [Mockito] class. - * - * For stubbing Unit functions (or Java void methods), see: [Mockito.doThrow]. + * This function is an alias for [Mockito.when]. So, for more detailed documentation, + * please refer to the Javadoc of that method in the [Mockito] class. + * For stubbing Unit functions (or Java void methods) with throwables, see: [Mockito.doThrow]. * * @param methodCall (regular or suspendable) lambda, wrapping the method/function call to be stubbed. * @return OngoingStubbing object used to stub fluently. @@ -105,15 +92,11 @@ fun whenever(methodCall: suspend CoroutineScope.() -> T): OngoingStubbing } /** - * Enables stubbing a (suspendable) kotlin functions. Use it when you want the mock to return - * a particular value when particular function is being called. - * The lambda with the function call to be stubbed can be either a synchronous (regular) - * or suspend lambda. + * Enables stubbing methods/function calls. + * In case of Kotlin function calls, these can be either synchronous or suspendable function calls. * * This is a deprecated alias for [whenever]. Please use [whenever] instead. * - * For more detailed documentation, please refer to [whenever]. - * * @param methodCall (regular or suspendable) lambda, wrapping the function call to be stubbed. * @return OngoingStubbing object used to stub fluently. * ***Do not*** create a reference to this returned object. @@ -124,14 +107,13 @@ fun wheneverBlocking(methodCall: suspend CoroutineScope.() -> T): OngoingStu } /** - * Sets a return value to be returned when the method/function is called. E.g: + * Sets a value to be returned when the stubbed method/function is being called. E.g: * * ```kotlin * whenever { mock.someMethod() } doReturn 10 * ``` * - * This function acts as an alias for Mockito's [OngoingStubbing.thenReturn], adding the infix - * functionality and extended type inference to it. + * This function is an alias for Mockito's [OngoingStubbing.thenReturn]. * * @param value return value for the method/function invocation. * @return OngoingStubbing object used to stub fluently. @@ -142,7 +124,8 @@ infix fun OngoingStubbing.doReturn(value: T): OngoingStubbing { } /** - * Sets a return values to be returned when the method/function is called consecutively. E.g: + * Sets values to be returned when the stubbed method/function is being called + * consecutively. E.g: * * ```kotlin * whenever { mock.someMethod() }.doReturn(10, 20) @@ -150,8 +133,7 @@ infix fun OngoingStubbing.doReturn(value: T): OngoingStubbing { * You can specify [values] to be returned on consecutive invocations. * In that case the last value determines the behavior of further consecutive invocations. * - * This function acts as an alias for Mockito's [OngoingStubbing.thenReturn], adding extended - * type inference to it. + * This function is an alias for Mockito's [OngoingStubbing.thenReturn]. * * @param value return value for the first method/function invocation. * @param values return values for the next method/function invocations. @@ -163,7 +145,8 @@ inline fun OngoingStubbing.doReturn(value: T, vararg values: T): } /** - * Sets a return values to be returned when the method/function is called consecutively. E.g: + * Sets values to be returned when the stubbed method/function is being called + * consecutively. E.g: * * ```kotlin * whenever { mock.someMethod() }.doReturnConsecutively(10, 20) @@ -171,8 +154,7 @@ inline fun OngoingStubbing.doReturn(value: T, vararg values: T): * You can specify [values] to be returned on consecutive invocations. * In that case the last value determines the behavior of further consecutive invocations. * - * This function acts as an alias for Mockito's [OngoingStubbing.thenReturn], adding extended - * type inference to it. + * This function is an alias for Mockito's [OngoingStubbing.thenReturn]. * * @param value return value for the first method/function invocation. * @param values return values for the next method/function invocations. @@ -184,7 +166,8 @@ inline fun OngoingStubbing.doReturnConsecutively(value: T, vararg } /** - * Sets a return values to be returned when the method/function is called consecutively. E.g: + * Sets values to be returned when the stubbed method/function is being called + * consecutively. E.g: * * ```kotlin * whenever { mock.someMethod() } doReturnConsecutively listOf(10, 20) @@ -192,8 +175,7 @@ inline fun OngoingStubbing.doReturnConsecutively(value: T, vararg * * The last value in [values] determines the behavior of further consecutive invocations. * - * This function acts as an alias for Mockito's [OngoingStubbing.thenReturn], adding the infix - * functionality and extended type inference to it. + * This function is an alias for Mockito's [OngoingStubbing.thenReturn]. * * @param values return values for the consecutive method/function invocations. * @return OngoingStubbing object used to stub fluently. @@ -204,14 +186,13 @@ inline infix fun OngoingStubbing.doReturnConsecutively(values: Li } /** - * Sets Throwable instance to be thrown when the method/function is called. E.g: + * Sets throwable to be thrown when the stubbed method/function is being called. E.g: * * ```kotlin * whenever { mock.someFunction() } doThrow RuntimeException() * ``` * - * This function acts as an alias for Mockito's [OngoingStubbing.thenThrow], adding the - * infix functionality to it. + * This function is an alias for Mockito's [OngoingStubbing.thenThrow]. * * @param throwable to be thrown on method/function invocations. * @return OngoingStubbing object used to stub fluently. @@ -222,7 +203,8 @@ infix fun OngoingStubbing.doThrow(throwable: Throwable): OngoingStubbing< } /** - * Sets Throwable instance(s) to be thrown when the method/function is called consecutively. E.g: + * Sets throwables to be thrown when the stubbed method/function is being called + * consecutively. E.g: * * ```kotlin * whenever { mock.someFunction() }.doThrow(RuntimeException(), IOException()) @@ -231,7 +213,7 @@ infix fun OngoingStubbing.doThrow(throwable: Throwable): OngoingStubbing< * You can specify [throwables] to be thrown for consecutive invocations. * In that case the last throwable determines the behavior of further consecutive invocations. * - * This function acts as an alias for Mockito's [OngoingStubbing.thenThrow]. + * This function is an alias for Mockito's [OngoingStubbing.thenThrow]. * * @param throwable to be thrown on the first method/function invocation. * @param throwables to be thrown on the next method/function invocations. @@ -243,14 +225,13 @@ fun OngoingStubbing.doThrow(throwable: Throwable, vararg throwables: Thro } /** - * Sets a Throwable type to be thrown when the method/function is called. E.g: + * Sets a throwable type to be thrown when the stubbed method/function is being called. E.g: * * ```kotlin * whenever { mock.someFunction() } doThrow IllegalArgumentException::class * ``` * - * This function acts as an alias for Mockito's [OngoingStubbing.thenThrow], accepting the - * throwable type as [KClass] and adding the infix functionality to it. + * This function is an alias for Mockito's [OngoingStubbing.thenThrow]. * * @param throwableType to be thrown on the method/function invocation. * @return OngoingStubbing object used to stub fluently. @@ -261,7 +242,7 @@ infix fun OngoingStubbing.doThrow(throwableType: KClass): } /** - * Sets a Throwable type to be thrown when the method is called consecutively. E.g: + * Sets throwable types to be thrown when the stubbed method is called consecutively. E.g: * * ```kotlin * whenever { mock.someFunction() }.doThrow(IllegalArgumentException::class, NullPointerException::class) @@ -270,8 +251,7 @@ infix fun OngoingStubbing.doThrow(throwableType: KClass): * You can specify [throwableTypes] to be thrown for consecutive invocations. * In that case the last throwable type determines the behavior of further consecutive invocations. * - * This function acts as an alias for Mockito's [OngoingStubbing.thenThrow], accepting the - * throwable types as [KClass]. + * This function is an alias for Mockito's [OngoingStubbing.thenThrow]. * * @param throwableType to be thrown on the first method/function invocation. * @param throwableTypes to be thrown on the next method/function invocations. @@ -283,15 +263,14 @@ fun OngoingStubbing.doThrow(throwableType: KClass, vararg } /** - * Sets a generic answer for when the method/function is called. E.g: + * Sets a generic answer to be applied when the stubbed method/function is being called. E.g: * * ```kotlin * val answer = Answer { "result" } * whenever { mock.someFunction() } doAnswer answer * ``` * - * This function acts as an alias for Mockito's [OngoingStubbing.thenAnswer], adding the infix - * functionality to it. + * This function is an alias for Mockito's [OngoingStubbing.thenAnswer]. * * @param answer to be applied on the method/function invocation. * @return OngoingStubbing object used to stub fluently. @@ -302,14 +281,14 @@ infix fun OngoingStubbing.doAnswer(answer: Answer<*>): OngoingStubbing } /** - * Sets an answer for when the method/function is called, specified by a lambda. E.g: + * Sets an answer to be applied when the stubbed method/function is being called, + * specified by a lambda. E.g: * * ```kotlin * whenever { mock.someFunction() } doAnswer { "result" } * ``` * - * This function acts as an alias for Mockito's [OngoingStubbing.thenAnswer], adding the infix - * functionality to it. + * This function is an alias for Mockito's [OngoingStubbing.thenAnswer]. * * @param answer to be applied on the method/function invocation. * @return OngoingStubbing object used to stub fluently. @@ -320,20 +299,19 @@ infix fun OngoingStubbing.doAnswer(answer: (KInvocationOnMock) -> T?): On } /** - * Sets an answer for when the suspendable function is called, specified by a suspendable lambda. E.g: + * Sets an answer to be applied when the stubbed suspendable function is being called, + * specified by a suspendable lambda. E.g: * * ```kotlin - * whenever { mock.someFunction() } doAnswer { + * whenever { mock.someFunction() } doSuspendableAnswer { * delay(1) * "result" * } * ``` * - * This function acts as an alias for Mockito's [OngoingStubbing.thenAnswer], adding the infix - * functionality to it. - * Also, this function will wrap the answer lambda in a SuspendableAnswer object, to wire the - * suspendable lambda properly into the Kotlin's coroutine context on invocation of the stubbed - * suspendable function. + * This function is an alias for Mockito's [OngoingStubbing.thenAnswer], but also taking + * extra steps to wire the suspendable lambda answer properly into the Kotlin's coroutine + * context of the stubbed suspendable function call. * * @param answer to be applied on the suspendable function invocation. * @return OngoingStubbing object used to stub fluently. diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt index cdedb2a5..c2139728 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt @@ -34,21 +34,16 @@ import kotlin.reflect.KClass /** * Sets a generic answer, specified with a lambda, to be applied in reverse stubbing. * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... - * * Example: * ```kotlin * doAnswer { "result" }.whenever(mock).someMethod() * ``` * - * This function acts as an alias for Mockito's [Mockito.doAnswer]. + * This function is an alias for Mockito's [Mockito.doAnswer]. * * See examples in javadoc for [Mockito] class * - * @param answer to answer to apply when the stubbed method/function is called. + * @param answer to answer to apply when the stubbed method/function is being called. * @return Stubber object used to stub fluently. */ fun doAnswer(answer: (KInvocationOnMock) -> T?): Stubber { @@ -58,11 +53,6 @@ fun doAnswer(answer: (KInvocationOnMock) -> T?): Stubber { /** * Sets a generic answer, specified with a suspendable lambda, to be applied in reverse stubbing. * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... - * * Example: * ```kotlin * doSuspendableAnswer { @@ -71,13 +61,13 @@ fun doAnswer(answer: (KInvocationOnMock) -> T?): Stubber { * }.whenever(mock).someMethod() * ``` * - * This function acts as an alias for Mockito's [Mockito.doAnswer], this function will also wrap - * the answer lambda in a SuspendableAnswer object, to wire the suspendable lambda properly - * into the Kotlin's coroutine context on invocation of the stubbed suspendable function. + * This function is an alias for Mockito's [Mockito.doAnswer], but also taking + * extra steps to wire the suspendable lambda answer properly into the Kotlin's coroutine + * context of the stubbed suspendable function call. * * See examples in javadoc for [Mockito] class * - * @param answer to answer to apply when the stubbed method/function is called. + * @param answer to answer to apply when the stubbed method/function is being called. * @return Stubber object used to stub fluently. */ fun doSuspendableAnswer(answer: suspend (KInvocationOnMock) -> T?): Stubber { @@ -87,19 +77,12 @@ fun doSuspendableAnswer(answer: suspend (KInvocationOnMock) -> T?): Stubber /** * Sets to call the real implementation of a method in a mock, to be applied in reverse stubbing. * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... - * * Example: * ```kotlin * doCallRealMethod().whenever(mock).someMethod() * ``` * - * This function acts as an alias for Mockito's [Mockito.doCallRealMethod]. - * - * See examples in javadoc for [Mockito] class + * This function is an alias for Mockito's [Mockito.doCallRealMethod]. * * @return Stubber object used to stub fluently. */ @@ -113,19 +96,12 @@ fun doCallRealMethod(): Stubber { * - stubbing consecutive calls with different behavior * - when spying a real object, suppress the real implementation of the spied object * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... - * * Example: * ```kotlin * doNothing().whenever(mock).someMethod() * ``` * - * This function acts as an alias for Mockito's [Mockito.doNothing]. - * - * See examples in javadoc for [Mockito] class + * This function is an alias for Mockito's [Mockito.doNothing]. * * @return Stubber object used to stub fluently. */ @@ -134,19 +110,14 @@ fun doNothing(): Stubber { } /** - * Sets to return the given value, to be applied in reverse stubbing. - * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... + * Sets a value to be returned, to be applied in reverse stubbing. * * Example: * ```kotlin * doReturn(10).whenever(mock).someMethod() * ``` * - * This function acts as an alias for Mockito's [Mockito.doReturn]. + * This function is an alias for Mockito's [Mockito.doReturn]. * * See examples in javadoc for [Mockito] class * @@ -158,19 +129,14 @@ fun doReturn(value: Any?): Stubber { } /** - * Sets to return the given values on consecutive invocations, to be applied in reverse stubbing. - * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... + * Sets values to be returned on consecutive invocations, to be applied in reverse stubbing. * * Example: * ```kotlin * doReturn(10, 20).whenever(mock).someMethod() * ``` * - * This function acts as an alias for Mockito's [Mockito.doReturn]. + * This function is an alias for Mockito's [Mockito.doReturn]. * * See examples in javadoc for [Mockito] class * @@ -183,19 +149,14 @@ fun doReturn(value: Any?, vararg values: Any?): Stubber { } /** - * Sets a Throwable to be thrown, to be applied in reverse stubbing. - * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... + * Sets a throwable to be thrown, to be applied in reverse stubbing. * * Example: * ```kotlin * doThrow(IllegalArgumentException()).whenever(mock).someMethod() * ``` * - * This function acts as an alias for Mockito's [Mockito.doThrow]. + * This function is an alias for Mockito's [Mockito.doThrow]. * * See examples in javadoc for [Mockito] class * @@ -207,19 +168,14 @@ fun doThrow(throwable: Throwable): Stubber { } /** - * Sets Throwables to be thrown consecutively, to be applied in reverse stubbing. - * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... + * Sets throwables to be thrown on consecutive calls, to be applied in reverse stubbing. * * Example: * ```kotlin * doThrow(RuntimeException(), IOException()).whenever(mock).someMethod() * ``` * - * This function acts as an alias for Mockito's [Mockito.doThrow]. + * This function is an alias for Mockito's [Mockito.doThrow]. * * You can specify [throwables] to be thrown for consecutive invocations. * In that case the last throwable determines the behavior of further consecutive invocations. @@ -235,19 +191,14 @@ fun doThrow(throwable: Throwable, vararg throwables: Throwable): Stubber { } /** - * Sets a Throwable types to be thrown, to be applied in reverse stubbing. - * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... + * Sets a throwable type to be thrown, to be applied in reverse stubbing. * * Example: * ```kotlin * doThrow(IllegalArgumentException::class).whenever(mock).someMethod() * ``` * - * This function acts as an alias for Mockito's [Mockito.doThrow]. + * This function is an alias for Mockito's [Mockito.doThrow]. * * See examples in javadoc for [Mockito] class * @@ -259,12 +210,7 @@ fun doThrow(throwableType: KClass): Stubber { } /** - * Sets a Throwable types to be thrown consecutively, to be applied in reverse stubbing. - * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... + * Sets throwable types to be thrown on consecutive calls, to be applied in reverse stubbing. * * Example: * ```kotlin @@ -273,7 +219,7 @@ fun doThrow(throwableType: KClass): Stubber { * .someMethod() * ``` * - * This function acts as an alias for Mockito's [Mockito.doThrow]. + * This function is an alias for Mockito's [Mockito.doThrow]. * * You can specify [throwableTypes] to be thrown for consecutive invocations. * In that case the last throwable type determines the behavior of further consecutive invocations. @@ -304,9 +250,8 @@ fun doThrow(throwableType: KClass, vararg throwableTypes: KClass< * doReturn(10).whenever(mock).someMethod() * ``` * - * This function acts as an alias for Mockito's [Mockito.doReturn]. - * - * See examples in javadoc for [Mockito] class + * This function is an alias for Mockito's [Mockito.`when`]. So, for more detailed + * documentation, please refer to the Javadoc of that method in the [Mockito] class. * * @param mock the mock to stub the method/function call on. * @return the mock used to stub the method/function call fluently. @@ -315,23 +260,23 @@ fun Stubber.whenever(mock: T) = `when`(mock)!! /** * Sets the mock and the method call to be stubbed. - * With this version of whenever you can reverse stub either synchronous as well as suspendable - * function calls. + * With this version of whenever you can reverse stub either synchronous or suspendable function calls. * * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to * problems in case of void methods (or Unit functions): the java compiler does not like void * methods inside brackets... * + * Warning: Only one method call can be stubbed in the function. Subsequent method calls are + * ignored! + * * Example: * ```kotlin * doReturn(10).whenever(mock) { someMethod() } * ``` - * Warning: Only one method call can be stubbed in the function. Subsequent method calls are ignored! - * - * This function acts as an alias for Mockito's [Mockito.`when`]. * - * See examples in javadoc for [Mockito] class + * This function is an alias for Mockito's [Mockito.`when`]. So, for more detailed + * documentation, please refer to the Javadoc of that method in the [Mockito] class. * * @param mock the mock to stub the method/function call on. * @param methodCall (regular or suspendable) lambda, wrapping the method/function call to be stubbed. @@ -344,24 +289,17 @@ fun Stubber.whenever(mock: T, methodCall: suspend T.() -> Unit) { /** * Sets the mock and the method call to be stubbed. - * With this version of whenever you can reverse stub either synchronous as well as suspendable - * function calls. + * With this version of whenever you can reverse stub either synchronous or suspendable function calls. * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... + * Warning: Only one method call can be stubbed in the function. Subsequent method calls are ignored! * * Example: * ```kotlin * doReturn(10).wheneverBlocking(mock) { someMethod() } * ``` - * Warning: Only one method call can be stubbed in the function. Subsequent method calls are ignored! - * + * * This function is an alias for [whenever]. * - * See examples in javadoc for [Mockito] class - * * @param mock the mock to stub the method/function call on. * @param methodCall (regular or suspendable) lambda, wrapping the method/function call to be stubbed. */ From 1f3c19e50aca56fa9ff07e80e35f812fad0662c7 Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Thu, 18 Dec 2025 18:56:37 +0100 Subject: [PATCH 6/6] Update KDoc documentation after review comments --- .../kotlin/org/mockito/kotlin/KStubbing.kt | 11 +++----- .../org/mockito/kotlin/OngoingStubbing.kt | 6 ++--- .../main/kotlin/org/mockito/kotlin/Stubber.kt | 26 ++++++++++--------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KStubbing.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KStubbing.kt index bca928de..b5e9181b 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KStubbing.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KStubbing.kt @@ -72,7 +72,7 @@ class KStubbing(val mock: T) { * * ```kotlin * stubbing(mock) { - * on (mock.someFunction()) doReturn 10 + * on(mock.someFunction()) doReturn 10 * } * ``` * @@ -219,18 +219,15 @@ class KStubbing(val mock: T) { * Stubs a method/function call in a reverse manner, as part of a mock being created. * You can reverse stub either synchronous as well as suspendable function calls. * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) + * to throw an exception. * * Example: * ```kotlin * mock { - * doReturn("Test").on { stringResult() } + * doThrow(RuntimeException()).on { string("test") } * } * ``` - * Warning: Only one method call can be stubbed in the function. Subsequent method calls are ignored! * * This function is an alias for [whenever]. Please use [whenever] instead. * diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt index 3c5ee76c..847467c8 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt @@ -47,7 +47,7 @@ import kotlin.reflect.KClass * whenever(mock.someFunction()) doReturn 10 * * //you can use flexible argument matchers, e.g: - * whenever(mock.someFunction(anyString())) doReturn 10 + * whenever(mock.someFunction(any())) doReturn 10 * ``` * * This function is an alias for [Mockito.when]. So, for more detailed documentation, @@ -76,7 +76,7 @@ fun whenever(methodCall: T): OngoingStubbing { * whenever { mock.someFunction() } doReturn 10 * * //you can use flexible argument matchers, e.g: - * whenever { mock.someFunction(anyString()) } doReturn 10 + * whenever { mock.someFunction(any()) } doReturn 10 * ``` * * This function is an alias for [Mockito.when]. So, for more detailed documentation, @@ -186,7 +186,7 @@ inline infix fun OngoingStubbing.doReturnConsecutively(values: Li } /** - * Sets throwable to be thrown when the stubbed method/function is being called. E.g: + * Sets a throwable to be thrown when the stubbed method/function is being called. E.g: * * ```kotlin * whenever { mock.someFunction() } doThrow RuntimeException() diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt index c2139728..cc23ce26 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt @@ -240,14 +240,14 @@ fun doThrow(throwableType: KClass, vararg throwableTypes: KClass< /** * Sets the mock to apply the reverse stubbing on. * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) + * to throw an exception. * * Example: * ```kotlin - * doReturn(10).whenever(mock).someMethod() + * mock { + * doThrow(RuntimeException()).whenever { string("test") } + * } * ``` * * This function is an alias for Mockito's [Mockito.`when`]. So, for more detailed @@ -262,17 +262,15 @@ fun Stubber.whenever(mock: T) = `when`(mock)!! * Sets the mock and the method call to be stubbed. * With this version of whenever you can reverse stub either synchronous or suspendable function calls. * - * Reverse stubbing is especially useful when stubbing a void method (or Unit function) as - * the regular approach of ongoing stubbing through [org.mockito.kotlin.whenever] leads to - * problems in case of void methods (or Unit functions): the java compiler does not like void - * methods inside brackets... + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) + * to throw an exception. * * Warning: Only one method call can be stubbed in the function. Subsequent method calls are * ignored! * * Example: * ```kotlin - * doReturn(10).whenever(mock) { someMethod() } + * doThrow(RuntimeException()).whenever(mock) { someMethod() } * ``` * * This function is an alias for Mockito's [Mockito.`when`]. So, for more detailed @@ -291,11 +289,15 @@ fun Stubber.whenever(mock: T, methodCall: suspend T.() -> Unit) { * Sets the mock and the method call to be stubbed. * With this version of whenever you can reverse stub either synchronous or suspendable function calls. * - * Warning: Only one method call can be stubbed in the function. Subsequent method calls are ignored! + * Reverse stubbing is especially useful when stubbing a void method (or Unit function) + * to throw an exception. * + * Warning: Only one method call can be stubbed in the function. Subsequent method calls are + * ignored! + * Example: * ```kotlin - * doReturn(10).wheneverBlocking(mock) { someMethod() } + * doThrow(RuntimeException()).wheneverBlocking(mock) { someMethod() } * ``` * * This function is an alias for [whenever].