aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOscar Guillén <Osguima3@users.noreply.github.com>2024-02-23 19:43:32 +0100
committerGitHub <noreply@github.com>2024-02-23 19:43:32 +0100
commit44cada251132c5d60bbbcf166ec0c58574e2c9cd (patch)
tree3d1986e676e4b450fd8b67ea641339e2f1be0094
parent017b08a4b9e1251f17bbabe0c6f7f7e81dbc79ed (diff)
downloadmockito-kotlin-44cada251132c5d60bbbcf166ec0c58574e2c9cd.tar.gz
Add support for destructured parameters in answers (#512)
-rw-r--r--mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KInvocationOnMock.kt39
-rw-r--r--mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt8
-rw-r--r--mockito-kotlin/src/main/kotlin/org/mockito/kotlin/internal/KAnswer.kt42
-rw-r--r--mockito-kotlin/src/main/kotlin/org/mockito/kotlin/internal/SuspendableAnswer.kt5
-rw-r--r--mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt15
-rw-r--r--tests/src/test/kotlin/test/OngoingStubbingTest.kt30
6 files changed, 129 insertions, 10 deletions
diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KInvocationOnMock.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KInvocationOnMock.kt
new file mode 100644
index 0000000..b433b76
--- /dev/null
+++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KInvocationOnMock.kt
@@ -0,0 +1,39 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2018 Niek Haarman
+ * Copyright (c) 2007 Mockito contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.mockito.kotlin
+
+import org.mockito.invocation.InvocationOnMock
+
+class KInvocationOnMock(
+ private val invocationOnMock: InvocationOnMock
+) : InvocationOnMock by invocationOnMock {
+
+ operator fun <T> component1(): T = invocationOnMock.getArgument(0)
+ operator fun <T> component2(): T = invocationOnMock.getArgument(1)
+ operator fun <T> component3(): T = invocationOnMock.getArgument(2)
+ operator fun <T> component4(): T = invocationOnMock.getArgument(3)
+ operator fun <T> component5(): T = invocationOnMock.getArgument(4)
+}
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 30966bf..39c6dcb 100644
--- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt
+++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/OngoingStubbing.kt
@@ -28,7 +28,7 @@ package org.mockito.kotlin
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
import org.mockito.Mockito
-import org.mockito.invocation.InvocationOnMock
+import org.mockito.kotlin.internal.KAnswer
import org.mockito.kotlin.internal.SuspendableAnswer
import org.mockito.stubbing.Answer
import org.mockito.stubbing.OngoingStubbing
@@ -132,10 +132,10 @@ infix fun <T> OngoingStubbing<T>.doAnswer(answer: Answer<*>): OngoingStubbing<T>
/**
* Sets a generic Answer for the method using a lambda.
*/
-infix fun <T> OngoingStubbing<T>.doAnswer(answer: (InvocationOnMock) -> T?): OngoingStubbing<T> {
- return thenAnswer(answer)
+infix fun <T> OngoingStubbing<T>.doAnswer(answer: (KInvocationOnMock) -> T?): OngoingStubbing<T> {
+ return thenAnswer(KAnswer(answer))
}
-infix fun <T> OngoingStubbing<T>.doSuspendableAnswer(answer: suspend (InvocationOnMock) -> T?): OngoingStubbing<T> {
+infix fun <T> OngoingStubbing<T>.doSuspendableAnswer(answer: suspend (KInvocationOnMock) -> T?): OngoingStubbing<T> {
return thenAnswer(SuspendableAnswer(answer))
}
diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/internal/KAnswer.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/internal/KAnswer.kt
new file mode 100644
index 0000000..ca62f04
--- /dev/null
+++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/internal/KAnswer.kt
@@ -0,0 +1,42 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2018 Niek Haarman
+ * Copyright (c) 2007 Mockito contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.mockito.kotlin.internal
+
+import org.mockito.invocation.InvocationOnMock
+import org.mockito.kotlin.KInvocationOnMock
+import org.mockito.stubbing.Answer
+
+/**
+ * This class wraps destructuring lambda into [Answer]
+ */
+@Suppress("UNCHECKED_CAST")
+internal class KAnswer<T>(
+ private val body: (KInvocationOnMock) -> T?
+) : Answer<T> {
+ override fun answer(invocation: InvocationOnMock): T {
+ return body(KInvocationOnMock(invocation)) as T
+ }
+}
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 3544cf6..239be2c 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
@@ -27,6 +27,7 @@ package org.mockito.kotlin.internal
import org.mockito.internal.invocation.InterceptedInvocation
import org.mockito.invocation.InvocationOnMock
+import org.mockito.kotlin.KInvocationOnMock
import org.mockito.stubbing.Answer
import kotlin.coroutines.Continuation
import kotlin.coroutines.intrinsics.startCoroutineUninterceptedOrReturn
@@ -36,7 +37,7 @@ import kotlin.coroutines.intrinsics.startCoroutineUninterceptedOrReturn
*/
@Suppress("UNCHECKED_CAST")
internal class SuspendableAnswer<T>(
- private val body: suspend (InvocationOnMock) -> T?
+ private val body: suspend (KInvocationOnMock) -> T?
) : Answer<T> {
override fun answer(invocation: InvocationOnMock?): T {
//all suspend functions/lambdas has Continuation as the last argument.
@@ -45,6 +46,6 @@ internal class SuspendableAnswer<T>(
val continuation = rawInvocation.rawArguments.last() as Continuation<T?>
// https://youtrack.jetbrains.com/issue/KT-33766#focus=Comments-27-3707299.0-0
- return body.startCoroutineUninterceptedOrReturn(invocation, continuation) as T
+ return body.startCoroutineUninterceptedOrReturn(KInvocationOnMock(invocation), continuation) as T
}
}
diff --git a/mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt b/mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt
index eea7fd4..363c223 100644
--- a/mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt
+++ b/mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt
@@ -3,10 +3,6 @@
package test
import com.nhaarman.expect.expect
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withContext
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.actor
import org.junit.Assert.assertEquals
@@ -266,6 +262,17 @@ class CoroutinesTest {
}
@Test
+ fun answerWithSuspendFunctionWithDestructuredArgs() = runBlocking {
+ val fixture: SomeInterface = mock()
+
+ whenever(fixture.suspendingWithArg(any())).doSuspendableAnswer { (i: Int) ->
+ withContext(Dispatchers.Default) { i }
+ }
+
+ assertEquals(5, fixture.suspendingWithArg(5))
+ }
+
+ @Test
fun willAnswerWithControlledSuspend() = runBlocking {
val fixture: SomeInterface = mock()
diff --git a/tests/src/test/kotlin/test/OngoingStubbingTest.kt b/tests/src/test/kotlin/test/OngoingStubbingTest.kt
index ab452a7..ba1bd32 100644
--- a/tests/src/test/kotlin/test/OngoingStubbingTest.kt
+++ b/tests/src/test/kotlin/test/OngoingStubbingTest.kt
@@ -202,6 +202,36 @@ class OngoingStubbingTest : TestBase() {
}
@Test
+ fun testOngoingStubbing_doAnswer_withDestructuredArgument() {
+ /* Given */
+ val mock = mock<Methods> {
+ on { stringResult(any()) } doAnswer { (s: String) -> "$s-result" }
+ }
+
+ /* When */
+ val result = mock.stringResult("argument")
+
+ /* Then */
+ expect(result).toBe("argument-result")
+ }
+
+ @Test
+ fun testOngoingStubbing_doAnswer_withDestructuredArguments() {
+ /* Given */
+ val mock = mock<Methods> {
+ on { varargBooleanResult(any(), any()) } doAnswer { (a: String, b: String) ->
+ a == b.trim()
+ }
+ }
+
+ /* When */
+ val result = mock.varargBooleanResult("argument", " argument ")
+
+ /* Then */
+ expect(result).toBe(true)
+ }
+
+ @Test
fun testMockStubbingAfterCreatingMock() {
val mock = mock<Methods>()