diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KInOrder.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KInOrder.kt new file mode 100644 index 00000000..152148ea --- /dev/null +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/KInOrder.kt @@ -0,0 +1,47 @@ +/* + * 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.InOrder +import org.mockito.verification.VerificationMode + +interface KInOrder: InOrder { + /** + * Verifies certain suspending behavior happened once in order. + * + * Warning: Only one method call can be verified in the function. + * Subsequent method calls are ignored! + */ + fun verifyBlocking(mock: T, f: suspend T.() -> Unit) + + /** + * Verifies certain suspending behavior happened at least once / exact number of times / never in order. + * + * Warning: Only one method call can be verified in the function. + * Subsequent method calls are ignored! + */ + fun verifyBlocking(mock: T, mode: VerificationMode, f: suspend T.() -> Unit) +} diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Verification.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Verification.kt index 2a16d0cb..04a477c8 100644 --- a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Verification.kt +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Verification.kt @@ -29,6 +29,7 @@ import org.mockito.kotlin.internal.createInstance import kotlinx.coroutines.runBlocking import org.mockito.InOrder import org.mockito.Mockito +import org.mockito.kotlin.internal.KInOrderDecorator import org.mockito.verification.VerificationAfterDelay import org.mockito.verification.VerificationMode import org.mockito.verification.VerificationWithTimeout @@ -188,32 +189,33 @@ fun ignoreStubs(vararg mocks: Any): Array { } /** - * Creates [InOrder] object that allows verifying mocks in order. + * Creates [KInOrder] object that allows verifying mocks in order. * - * Alias for [Mockito.inOrder]. + * Wrapper for [Mockito.inOrder] that also allows to verify suspending method calls. */ -fun inOrder(vararg mocks: Any): InOrder { - return Mockito.inOrder(*mocks)!! +fun inOrder(vararg mocks: Any): KInOrder { + return KInOrderDecorator(Mockito.inOrder(*mocks)!!) } /** - * Creates [InOrder] object that allows verifying mocks in order. + * Creates [KInOrder] object that allows verifying mocks in order. * Accepts a lambda to allow easy evaluation. * - * Alias for [Mockito.inOrder]. + * Wrapper for [Mockito.inOrder] that also allows to verify suspending method calls. */ inline fun inOrder( vararg mocks: Any, - evaluation: InOrder.() -> Unit + evaluation: KInOrder.() -> Unit ) { - Mockito.inOrder(*mocks).evaluation() + KInOrderDecorator(Mockito.inOrder(*mocks)).evaluation() } /** - * Allows [InOrder] verification for a single mocked instance: + * Allows [KInOrder] verification for a single mocked instance: * * mock.inOrder { * verify().foo() + * verifyBlocking { bar() } * } * */ @@ -221,9 +223,33 @@ inline fun T.inOrder(block: InOrderOnType.() -> Any) { block.invoke(InOrderOnType(this)) } -class InOrderOnType(private val t: T) : InOrder by inOrder(t as Any) { +class InOrderOnType(private val t: T) : KInOrder by inOrder(t as Any) { + /** + * Verifies certain behavior happened once in order. + */ fun verify(): T = verify(t) + + /** + * Verifies certain behavior happened at least once / exact number of times / never in order. + */ + fun verify(mode: VerificationMode): T = verify(t, mode) + + /** + * Verifies certain suspending behavior happened once in order. + * + * Warning: Only one method call can be verified in the function. + * Subsequent method calls are ignored! + */ + fun verifyBlocking(f: suspend T.() -> Unit) = verifyBlocking(t, f) + + /** + * Verifies certain suspending behavior happened at least once / exact number of times / never in order. + * + * Warning: Only one method call can be verified in the function. + * Subsequent method calls are ignored! + */ + fun verifyBlocking(mode: VerificationMode, f: suspend T.() -> Unit) = verifyBlocking(t, mode, f) } /** diff --git a/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/internal/KInOrderDecorator.kt b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/internal/KInOrderDecorator.kt new file mode 100644 index 00000000..6f591f42 --- /dev/null +++ b/mockito-kotlin/src/main/kotlin/org/mockito/kotlin/internal/KInOrderDecorator.kt @@ -0,0 +1,43 @@ +/* + * 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 kotlinx.coroutines.runBlocking +import org.mockito.InOrder +import org.mockito.kotlin.KInOrder +import org.mockito.verification.VerificationMode + +class KInOrderDecorator(private val inOrder: InOrder) : KInOrder, InOrder by inOrder { + override fun verifyBlocking(mock: T, f: suspend T.() -> Unit) { + val m = verify(mock) + runBlocking { m.f() } + } + + override fun verifyBlocking(mock: T, mode: VerificationMode, f: suspend T.() -> Unit) { + val m = verify(mock, mode) + runBlocking { m.f() } + } +} diff --git a/mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt b/mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt index 51084e0f..c43d4269 100644 --- a/mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt +++ b/mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt @@ -10,7 +10,9 @@ import kotlinx.coroutines.withContext import kotlinx.coroutines.* import kotlinx.coroutines.channels.actor import org.junit.Assert.assertEquals +import org.junit.Assert.assertThrows import org.junit.Test +import org.mockito.InOrder import org.mockito.kotlin.* import java.util.* @@ -255,6 +257,134 @@ class CoroutinesTest { assertEquals(5, asyncTask.await()) } } + + @Test + fun inOrderRemainsCompatible() { + /* Given */ + val fixture: SomeInterface = mock() + + /* When */ + val inOrder = inOrder(fixture) + + /* Then */ + expect(inOrder).toBeInstanceOf() + } + + @Test + fun inOrderSuspendingCalls() { + /* Given */ + val fixtureOne: SomeInterface = mock() + val fixtureTwo: SomeInterface = mock() + + /* When */ + runBlocking { + fixtureOne.suspending() + fixtureTwo.suspending() + } + + /* Then */ + val inOrder = inOrder(fixtureOne, fixtureTwo) + inOrder.verifyBlocking(fixtureOne) { suspending() } + inOrder.verifyBlocking(fixtureTwo) { suspending() } + } + + @Test + fun inOrderSuspendingCallsFailure() { + /* Given */ + val fixtureOne: SomeInterface = mock() + val fixtureTwo: SomeInterface = mock() + + /* When */ + runBlocking { + fixtureOne.suspending() + fixtureTwo.suspending() + } + + /* Then */ + val inOrder = inOrder(fixtureOne, fixtureTwo) + inOrder.verifyBlocking(fixtureTwo) { suspending() } + assertThrows(AssertionError::class.java) { + inOrder.verifyBlocking(fixtureOne) { suspending() } + } + } + + @Test + fun inOrderBlockSuspendingCalls() { + /* Given */ + val fixtureOne: SomeInterface = mock() + val fixtureTwo: SomeInterface = mock() + + /* When */ + runBlocking { + fixtureOne.suspending() + fixtureTwo.suspending() + } + + /* Then */ + inOrder(fixtureOne, fixtureTwo) { + verifyBlocking(fixtureOne) { suspending() } + verifyBlocking(fixtureTwo) { suspending() } + } + } + + @Test + fun inOrderBlockSuspendingCallsFailure() { + /* Given */ + val fixtureOne: SomeInterface = mock() + val fixtureTwo: SomeInterface = mock() + + /* When */ + runBlocking { + fixtureOne.suspending() + fixtureTwo.suspending() + } + + /* Then */ + inOrder(fixtureOne, fixtureTwo) { + verifyBlocking(fixtureTwo) { suspending() } + assertThrows(AssertionError::class.java) { + verifyBlocking(fixtureOne) { suspending() } + } + } + } + + @Test + fun inOrderOnObjectSuspendingCalls() { + /* Given */ + val fixture: SomeInterface = mock() + + /* When */ + runBlocking { + fixture.suspendingWithArg(1) + fixture.suspendingWithArg(2) + } + + /* Then */ + fixture.inOrder { + verifyBlocking { suspendingWithArg(1) } + verifyBlocking { suspendingWithArg(2) } + } + } + + @Test + fun inOrderOnObjectSuspendingCallsFailure() { + /* Given */ + val fixture: SomeInterface = mock() + + /* When */ + runBlocking { + fixture.suspendingWithArg(1) + fixture.suspendingWithArg(2) + } + + /* Then */ + fixture.inOrder { + verifyBlocking { suspendingWithArg(2) } + assertThrows(AssertionError::class.java) { + verifyBlocking { suspendingWithArg(1) } + } + } + } } interface SomeInterface {