From d16efb7a5acc907331daaca5de2a43319b97e695 Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Fri, 28 Nov 2025 15:23:56 +0100 Subject: [PATCH 01/10] Bump library "org.jetbrains.kotlinx:kotlinx-coroutines-test" from a RC of 1.9.0 to a released version 1.10.2 --- tests/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/build.gradle b/tests/build.gradle index dd447d9..6bb524c 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -19,7 +19,7 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation "com.nhaarman:expect.kt:1.0.1" - testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0-RC" + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2" } tasks.withType(KotlinCompile).configureEach { From 5169b120bbf4df5d4b7b77707ebc8713f67a1fdf Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Fri, 28 Nov 2025 15:53:23 +0100 Subject: [PATCH 02/10] Move class CoroutinesTest to proper test module --- {mockito-kotlin => tests}/src/test/kotlin/test/CoroutinesTest.kt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {mockito-kotlin => tests}/src/test/kotlin/test/CoroutinesTest.kt (100%) diff --git a/mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt b/tests/src/test/kotlin/test/CoroutinesTest.kt similarity index 100% rename from mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt rename to tests/src/test/kotlin/test/CoroutinesTest.kt From e30d739e655f51943484781265c06df2b320a06b Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Fri, 28 Nov 2025 22:12:35 +0100 Subject: [PATCH 03/10] Move generic test cases about the stubbing mechanics from class OngoingStubbingTest to new class StubbingTest --- .../test/kotlin/test/OngoingStubbingTest.kt | 140 +++--------------- tests/src/test/kotlin/test/StubbingTest.kt | 129 ++++++++++++++++ 2 files changed, 146 insertions(+), 123 deletions(-) create mode 100644 tests/src/test/kotlin/test/StubbingTest.kt diff --git a/tests/src/test/kotlin/test/OngoingStubbingTest.kt b/tests/src/test/kotlin/test/OngoingStubbingTest.kt index 2891720..c4afda7 100644 --- a/tests/src/test/kotlin/test/OngoingStubbingTest.kt +++ b/tests/src/test/kotlin/test/OngoingStubbingTest.kt @@ -6,28 +6,20 @@ import com.nhaarman.expect.fail import org.junit.Assume.assumeFalse import org.junit.Test import org.mockito.Mockito -import org.mockito.exceptions.misusing.UnfinishedStubbingException import org.mockito.kotlin.any -import org.mockito.kotlin.argThat -import org.mockito.kotlin.check import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.doReturnConsecutively import org.mockito.kotlin.doThrow import org.mockito.kotlin.mock -import org.mockito.kotlin.stub -import org.mockito.kotlin.stubbing -import org.mockito.kotlin.whenever import org.mockito.stubbing.Answer class OngoingStubbingTest : TestBase() { - @Test fun testOngoingStubbing_methodCall() { /* Given */ - val mock = mock() - mock { - on(mock.stringResult()).doReturn("A") + val mock = mock { + on { stringResult() } doReturn "A" } /* When */ @@ -76,7 +68,7 @@ class OngoingStubbingTest : TestBase() { /* When */ mock.builderMethod() fail("No exception thrown") - } catch (e: IllegalArgumentException) { + } catch (_: IllegalArgumentException) { } } @@ -91,7 +83,7 @@ class OngoingStubbingTest : TestBase() { /* When */ mock.builderMethod() fail("No exception thrown") - } catch (e: IllegalArgumentException) { + } catch (_: IllegalArgumentException) { } } @@ -109,14 +101,14 @@ class OngoingStubbingTest : TestBase() { /* When */ mock.builderMethod() fail("No exception thrown") - } catch (e: IllegalArgumentException) { + } catch (_: IllegalArgumentException) { } try { /* When */ mock.builderMethod() fail("No exception thrown") - } catch (e: UnsupportedOperationException) { + } catch (_: UnsupportedOperationException) { } } @@ -134,14 +126,14 @@ class OngoingStubbingTest : TestBase() { /* When */ mock.builderMethod() fail("No exception thrown") - } catch (e: IllegalArgumentException) { + } catch (_: IllegalArgumentException) { } try { /* When */ mock.builderMethod() fail("No exception thrown") - } catch (e: UnsupportedOperationException) { + } catch (_: UnsupportedOperationException) { } } @@ -162,8 +154,9 @@ class OngoingStubbingTest : TestBase() { @Test fun testOngoingStubbing_doAnswer_instance() { /* Given */ + val answer = Answer { "result" } val mock = mock { - on { stringResult() } doAnswer Answer { "result" } + on { stringResult() } doAnswer answer } /* When */ @@ -231,75 +224,6 @@ class OngoingStubbingTest : TestBase() { expect(result).toBe(true) } - @Test - fun testMockStubbingAfterCreatingMock() { - val mock = mock() - - //create stub after creation of mock - mock.stub { - on { stringResult() } doReturn "result" - } - - /* When */ - val result = mock.stringResult() - - /* Then */ - expect(result).toBe("result") - } - - @Test - fun testOverrideDefaultStub() { - /* Given mock with stub */ - val mock = mock { - on { stringResult() } doReturn "result1" - } - - /* override stub */ - mock.stub { - on { stringResult() } doReturn "result2" - } - - /* When */ - val result = mock.stringResult() - - /* Then */ - expect(result).toBe("result2") - } - - @Test - fun stubbingTwiceWithArgumentMatchers() { - /* When */ - val mock = mock { - on { stringResult(argThat { this == "A" }) } doReturn "A" - on { stringResult(argThat { this == "B" }) } doReturn "B" - } - - /* Then */ - expect(mock.stringResult("A")).toBe("A") - expect(mock.stringResult("B")).toBe("B") - } - - @Test - fun stubbingRealObject() { - val notAMock = "" - - /* Expect */ - expectErrorWithMessage("is not a mock!").on { - notAMock.stub { } - } - } - - @Test - fun stubbingTwiceWithCheckArgumentMatchers_throwsException() { - /* Expect */ - expectErrorWithMessage("null").on { - mock { - on { stringResult(check { }) } doReturn "A" - on { stringResult(check { }) } doReturn "B" - } - } - } - @Test fun doReturn_withSingleItemList() { /* Given */ @@ -312,18 +236,6 @@ class OngoingStubbingTest : TestBase() { expect(mock.stringResult()).toBe("b") } - @Test - fun doReturn_throwsNPE() { - assumeFalse(mockMakerInlineEnabled()) - expectErrorWithMessage("look at the stack trace below") on { - - /* When */ - mock { - on { throwsNPE() } doReturn "result" - } - } - } - @Test fun doReturn_withGenericIntReturnType_onGeneric() { /* Given */ @@ -345,32 +257,14 @@ class OngoingStubbingTest : TestBase() { } @Test - fun stubbingExistingMock() { - /* Given */ - val mock = mock() - - /* When */ - stubbing(mock) { - on { stringResult() } doReturn "result" - } - - /* Then */ - expect(mock.stringResult()).toBe("result") - } - - @Test - fun testMockitoStackOnUnfinishedStubbing() { - /* Given */ - val mock = mock() - whenever(mock.stringResult()) + fun doReturn_throwsNPE() { + assumeFalse(mockMakerInlineEnabled()) + expectErrorWithMessage("look at the stack trace below") on { - /* When */ - try { - mock.stringResult() - } catch (e: UnfinishedStubbingException) { - /* Then */ - expect(e.message).toContain("Unfinished stubbing detected here:") - expect(e.message).toContain("-> at test.OngoingStubbingTest.testMockitoStackOnUnfinishedStubbing") + /* When */ + mock { + on { throwsNPE() } doReturn "result" + } } } } diff --git a/tests/src/test/kotlin/test/StubbingTest.kt b/tests/src/test/kotlin/test/StubbingTest.kt new file mode 100644 index 0000000..7fc3d1e --- /dev/null +++ b/tests/src/test/kotlin/test/StubbingTest.kt @@ -0,0 +1,129 @@ +package test + +import com.nhaarman.expect.expect +import com.nhaarman.expect.expectErrorWithMessage +import org.junit.Test +import org.mockito.exceptions.misusing.UnfinishedStubbingException +import org.mockito.kotlin.argThat +import org.mockito.kotlin.check +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.stub +import org.mockito.kotlin.stubbing +import org.mockito.kotlin.whenever + + +class StubbingTest { + @Test + fun testOngoingStubbing_methodCall() { + /* Given */ + val mock = mock { + on { stringResult() } doReturn "A" + } + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun stubbingExistingMock() { + /* Given */ + val mock = mock() + + /* When */ + stubbing(mock) { + on { stringResult() } doReturn "result" + } + + /* Then */ + expect(mock.stringResult()).toBe("result") + } + + @Test + fun testMockStubbingAfterCreatingMock() { + val mock = mock() + + //create stub after creation of mock + mock.stub { + on { stringResult() } doReturn "result" + } + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("result") + } + + @Test + fun testOverrideDefaultStub() { + /* Given mock with stub */ + val mock = mock { + on { stringResult() } doReturn "result1" + } + + /* override stub */ + mock.stub { + on { stringResult() } doReturn "result2" + } + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("result2") + } + + @Test + fun stubbingTwiceWithArgumentMatchers() { + /* When */ + val mock = mock { + on { stringResult(argThat { this == "A" }) } doReturn "A" + on { stringResult(argThat { this == "B" }) } doReturn "B" + } + + /* Then */ + expect(mock.stringResult("A")).toBe("A") + expect(mock.stringResult("B")).toBe("B") + } + + @Test + fun stubbingRealObject() { + val notAMock = "" + + /* Expect */ + expectErrorWithMessage("is not a mock!").on { + notAMock.stub { } + } + } + + @Test + fun stubbingTwiceWithCheckArgumentMatchers_throwsException() { + /* Expect */ + expectErrorWithMessage("null").on { + mock { + on { stringResult(check { }) } doReturn "A" + on { stringResult(check { }) } doReturn "B" + } + } + } + + @Test + fun testMockitoStackOnUnfinishedStubbing() { + /* Given */ + val mock = mock() + whenever(mock.stringResult()) + + /* When */ + try { + mock.stringResult() + } catch (e: UnfinishedStubbingException) { + /* Then */ + expect(e.message).toContain("Unfinished stubbing detected here:") + expect(e.message).toContain("-> at test.StubbingTest.testMockitoStackOnUnfinishedStubbing") + } + } +} From fd53e69864d7caf554a411825ec035c319818624 Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Fri, 28 Nov 2025 18:32:43 +0100 Subject: [PATCH 04/10] Rename test methods to backtick strings starting with 'should' --- .../test/kotlin/test/OngoingStubbingTest.kt | 32 +++++++++---------- tests/src/test/kotlin/test/StubbingTest.kt | 18 +++++------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/src/test/kotlin/test/OngoingStubbingTest.kt b/tests/src/test/kotlin/test/OngoingStubbingTest.kt index c4afda7..e361067 100644 --- a/tests/src/test/kotlin/test/OngoingStubbingTest.kt +++ b/tests/src/test/kotlin/test/OngoingStubbingTest.kt @@ -16,7 +16,7 @@ import org.mockito.stubbing.Answer class OngoingStubbingTest : TestBase() { @Test - fun testOngoingStubbing_methodCall() { + fun `should stub function call`() { /* Given */ val mock = mock { on { stringResult() } doReturn "A" @@ -30,7 +30,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun testOngoingStubbing_builder() { + fun `should stub builder method returning mock itself`() { /* Given */ val mock = mock { mock -> on { builderMethod() } doReturn mock @@ -44,7 +44,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun testOngoingStubbing_nullable() { + fun `should stub function call with nullable result`() { /* Given */ val mock = mock { on { nullableStringResult() } doReturn "Test" @@ -58,7 +58,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun testOngoingStubbing_doThrow() { + fun `should throw exception instance on function call`() { /* Given */ val mock = mock { on { builderMethod() } doThrow IllegalArgumentException() @@ -73,7 +73,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun testOngoingStubbing_doThrowClass() { + fun `should throw exception class on function call`() { /* Given */ val mock = mock { on { builderMethod() } doThrow IllegalArgumentException::class @@ -88,7 +88,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun testOngoingStubbing_doThrowVarargs() { + fun `should throw exception instances on consecutive function calls`() { /* Given */ val mock = mock { on { builderMethod() }.doThrow( @@ -113,7 +113,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun testOngoingStubbing_doThrowClassVarargs() { + fun `should throw exception classes on consecutive function calls`() { /* Given */ val mock = mock { on { builderMethod() }.doThrow( @@ -138,7 +138,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun testOngoingStubbing_doAnswer_lambda() { + fun `should stub function call with result from lambda`() { /* Given */ val mock = mock { on { stringResult() } doAnswer { "result" } @@ -152,7 +152,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun testOngoingStubbing_doAnswer_instance() { + fun `should stub function call with result from an answer instance`() { /* Given */ /* Given */ val answer = Answer { "result" } val mock = mock { @@ -167,7 +167,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun testOngoingStubbing_doAnswer_returnsSelf() { + fun `should stub builder method returning mock itself via answer`() { /* Given */ val mock = mock { on { builderMethod() } doAnswer Mockito.RETURNS_SELF @@ -181,7 +181,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun testOngoingStubbing_doAnswer_withArgument() { + fun `should stub function call with result from lambda with argument`() { /* Given */ val mock = mock { on { stringResult(any()) } doAnswer { "${it.arguments[0]}-result" } @@ -195,7 +195,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun testOngoingStubbing_doAnswer_withDestructuredArgument() { + fun `should stub function call with result from lambda with deconstructed argument`() { /* Given */ val mock = mock { on { stringResult(any()) } doAnswer { (s: String) -> "$s-result" } @@ -209,7 +209,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun testOngoingStubbing_doAnswer_withDestructuredArguments() { + fun `should stub function call with result from lambda with deconstructed arguments`() { /* Given */ val mock = mock { on { varargBooleanResult(any(), any()) } doAnswer { (a: String, b: String) -> @@ -225,7 +225,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun doReturn_withSingleItemList() { + fun `should stub consecutive function calls by a list of answers`() { /* Given */ val mock = mock { on { stringResult() } doReturnConsecutively listOf("a", "b") @@ -237,7 +237,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun doReturn_withGenericIntReturnType_onGeneric() { + fun `should stub function call with integer result`() { /* Given */ val mock = mock> { onGeneric { genericMethod() } doReturn 2 @@ -248,7 +248,7 @@ class OngoingStubbingTest : TestBase() { } @Test - fun doReturn_withGenericNullableReturnType_onGeneric() { + fun `should stub nullable function call with string result`() { val m = mock> { onGeneric { nullableReturnType() } doReturn "Test" } diff --git a/tests/src/test/kotlin/test/StubbingTest.kt b/tests/src/test/kotlin/test/StubbingTest.kt index 7fc3d1e..20e308f 100644 --- a/tests/src/test/kotlin/test/StubbingTest.kt +++ b/tests/src/test/kotlin/test/StubbingTest.kt @@ -15,7 +15,7 @@ import org.mockito.kotlin.whenever class StubbingTest { @Test - fun testOngoingStubbing_methodCall() { + fun `should stub function call`() { /* Given */ val mock = mock { on { stringResult() } doReturn "A" @@ -29,7 +29,7 @@ class StubbingTest { } @Test - fun stubbingExistingMock() { + fun `should stub already existing mock, using stubbing function`() { /* Given */ val mock = mock() @@ -43,7 +43,7 @@ class StubbingTest { } @Test - fun testMockStubbingAfterCreatingMock() { + fun `should stub already existing mock, using stub extension function`() { val mock = mock() //create stub after creation of mock @@ -59,7 +59,7 @@ class StubbingTest { } @Test - fun testOverrideDefaultStub() { + fun `should override default stub of mock`() { /* Given mock with stub */ val mock = mock { on { stringResult() } doReturn "result1" @@ -78,7 +78,7 @@ class StubbingTest { } @Test - fun stubbingTwiceWithArgumentMatchers() { + fun `should stub 2 method calls determined by ArgumentMatchers`() { /* When */ val mock = mock { on { stringResult(argThat { this == "A" }) } doReturn "A" @@ -91,7 +91,7 @@ class StubbingTest { } @Test - fun stubbingRealObject() { + fun `should throw when trying to stub a real object with stub extension method`() { val notAMock = "" /* Expect */ @@ -101,7 +101,7 @@ class StubbingTest { } @Test - fun stubbingTwiceWithCheckArgumentMatchers_throwsException() { + fun `should throw when check ArgumentMatcher is applied twice`() { /* Expect */ expectErrorWithMessage("null").on { mock { @@ -112,7 +112,7 @@ class StubbingTest { } @Test - fun testMockitoStackOnUnfinishedStubbing() { + fun `should throw when stubbing is incomplete`() { /* Given */ val mock = mock() whenever(mock.stringResult()) @@ -123,7 +123,7 @@ class StubbingTest { } catch (e: UnfinishedStubbingException) { /* Then */ expect(e.message).toContain("Unfinished stubbing detected here:") - expect(e.message).toContain("-> at test.StubbingTest.testMockitoStackOnUnfinishedStubbing") + expect(e.message).toContain("-> at test.StubbingTest.should throw when stubbing is incomplete") } } } From 4fbea13484a137001e7c96e1e3eb22e385c277bb Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Fri, 28 Nov 2025 21:12:49 +0100 Subject: [PATCH 05/10] Rename the test interface Methods to SynchronousFunctions + move the suspend functions of this test interface to a new interface SuspendFunctions --- .../kotlin/test/AdditionalMatchersTest.kt | 36 ++-- .../test/kotlin/test/ArgumentCaptorTest.kt | 28 +-- tests/src/test/kotlin/test/BDDMockitoTest.kt | 22 +- tests/src/test/kotlin/test/Classes.kt | 14 +- tests/src/test/kotlin/test/MatchersTest.kt | 188 +++++++++--------- tests/src/test/kotlin/test/MockingTest.kt | 60 +++--- .../test/kotlin/test/OngoingStubbingTest.kt | 24 +-- tests/src/test/kotlin/test/StubberTest.kt | 14 +- tests/src/test/kotlin/test/StubbingTest.kt | 10 +- .../src/test/kotlin/test/VerificationTest.kt | 16 +- 10 files changed, 206 insertions(+), 206 deletions(-) diff --git a/tests/src/test/kotlin/test/AdditionalMatchersTest.kt b/tests/src/test/kotlin/test/AdditionalMatchersTest.kt index bb6f5ed..9e42ed6 100644 --- a/tests/src/test/kotlin/test/AdditionalMatchersTest.kt +++ b/tests/src/test/kotlin/test/AdditionalMatchersTest.kt @@ -7,7 +7,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testGeq() { - mock().apply { + mock().apply { int(1) verify(this).int(geq(0)) verify(this).int(geq(1)) @@ -17,7 +17,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testLeq() { - mock().apply { + mock().apply { int(1) verify(this).int(leq(2)) verify(this).int(leq(1)) @@ -27,7 +27,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testGt() { - mock().apply { + mock().apply { int(1) verify(this).int(gt(0)) verify(this, never()).int(gt(1)) @@ -36,7 +36,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testLt() { - mock().apply { + mock().apply { int(1) verify(this).int(lt(2)) verify(this, never()).int(lt(1)) @@ -45,7 +45,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testCmpEq() { - mock().apply { + mock().apply { int(1) verify(this).int(cmpEq(1)) verify(this, never()).int(cmpEq(2)) @@ -54,7 +54,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testAryEqBoolean() { - mock().apply { + mock().apply { booleanArray(booleanArrayOf(true, false, true)) verify(this).booleanArray(aryEq(booleanArrayOf(true, false, true))) verify(this, never()).booleanArray(aryEq(booleanArrayOf(true, false))) @@ -63,7 +63,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testAryEqByte() { - mock().apply { + mock().apply { byteArray(byteArrayOf(1, 2, 3)) verify(this).byteArray(aryEq(byteArrayOf(1, 2, 3))) verify(this, never()).byteArray(aryEq(byteArrayOf(1, 2))) @@ -72,7 +72,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testAryEqShort() { - mock().apply { + mock().apply { shortArray(shortArrayOf(1, 2, 3)) verify(this).shortArray(aryEq(shortArrayOf(1, 2, 3))) verify(this, never()).shortArray(aryEq(shortArrayOf(1, 2))) @@ -81,7 +81,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testAryEqInt() { - mock().apply { + mock().apply { intArray(intArrayOf(1, 2, 3)) verify(this).intArray(aryEq(intArrayOf(1, 2, 3))) verify(this, never()).intArray(aryEq(intArrayOf(1, 2))) @@ -90,7 +90,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testAryEqLong() { - mock().apply { + mock().apply { longArray(longArrayOf(1, 2, 3)) verify(this).longArray(aryEq(longArrayOf(1, 2, 3))) verify(this, never()).longArray(aryEq(longArrayOf(1, 2))) @@ -99,7 +99,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testAryEqChar() { - mock().apply { + mock().apply { charArray(charArrayOf('1', '2', '3')) verify(this).charArray(aryEq(charArrayOf('1', '2', '3'))) verify(this, never()).charArray(aryEq(charArrayOf('1', '2'))) @@ -108,7 +108,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testAryEqFloat() { - mock().apply { + mock().apply { floatArray(floatArrayOf(1f, 2f, 3.4f)) verify(this).floatArray(aryEq(floatArrayOf(1f, 2f, 3.4f))) verify(this, never()).floatArray(aryEq(floatArrayOf(1f, 2f))) @@ -117,7 +117,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testAryEqDouble() { - mock().apply { + mock().apply { doubleArray(doubleArrayOf(1.0, 2.0, 3.4)) verify(this).doubleArray(aryEq(doubleArrayOf(1.0, 2.0, 3.4))) verify(this, never()).doubleArray(aryEq(doubleArrayOf(1.0, 2.0))) @@ -126,7 +126,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testAryEq() { - mock().apply { + mock().apply { stringArray(arrayOf("Hello", "there")) verify(this).stringArray(aryEq(arrayOf("Hello", "there"))) verify(this, never()).stringArray(aryEq(arrayOf("Hello"))) @@ -135,7 +135,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testFind() { - mock().apply { + mock().apply { string("Hello") verify(this).string(find("l+o$".toRegex())) verify(this, never()).string(find("l$".toRegex())) @@ -144,7 +144,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testAnd() { - mock().apply { + mock().apply { int(5) verify(this).int(and(geq(4), leq(6))) verify(this, never()).int(and(geq(4), leq(4))) @@ -153,7 +153,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testOr() { - mock().apply { + mock().apply { int(5) verify(this).int(and(gt(4), lt(6))) verify(this, never()).int(and(gt(4), lt(4))) @@ -162,7 +162,7 @@ class AdditionalCaptorsTest : TestBase() { @Test fun testNot() { - mock().apply { + mock().apply { int(5) verify(this).int(not(eq(4))) verify(this, never()).int(not(eq(5))) diff --git a/tests/src/test/kotlin/test/ArgumentCaptorTest.kt b/tests/src/test/kotlin/test/ArgumentCaptorTest.kt index 14382a5..891f7ff 100644 --- a/tests/src/test/kotlin/test/ArgumentCaptorTest.kt +++ b/tests/src/test/kotlin/test/ArgumentCaptorTest.kt @@ -101,7 +101,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_withNullValue_usingNonNullable() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.nullableString(null) @@ -115,7 +115,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_withNullValue_usingNullable() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.nullableString(null) @@ -173,7 +173,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_multipleValuesIncludingNull() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.nullableString("test") @@ -188,7 +188,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_callProperties() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.int(1) @@ -211,7 +211,7 @@ class ArgumentCaptorTest : TestBase() { @Test(expected = IndexOutOfBoundsException::class) fun argumentCaptor_callPropertyNotAvailable() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.int(1) @@ -259,7 +259,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_vararg() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.varargBooleanResult("a", "b", "c") @@ -273,7 +273,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_empty_vararg() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.varargBooleanResult() @@ -287,7 +287,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_arg_vararg() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.argAndVararg("first", "a", "b", "c") @@ -301,7 +301,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_intarray() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.intArray(intArrayOf(1, 2, 3)) @@ -315,7 +315,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_array() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.stringArray(arrayOf("a", "b", "c")) @@ -329,7 +329,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_empty_array() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.stringArray(arrayOf()) @@ -343,7 +343,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_value_class() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() val valueClass = ValueClass("Content") /* When */ @@ -358,7 +358,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_value_class_withNullValue_usingNonNullable() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.nullableValueClass(null) @@ -372,7 +372,7 @@ class ArgumentCaptorTest : TestBase() { @Test fun argumentCaptor_value_class_withNullValue_usingNullable() { /* Given */ - val m: Methods = mock() + val m: SynchronousFunctions = mock() /* When */ m.nullableValueClass(null) diff --git a/tests/src/test/kotlin/test/BDDMockitoTest.kt b/tests/src/test/kotlin/test/BDDMockitoTest.kt index 0472163..457bf40 100644 --- a/tests/src/test/kotlin/test/BDDMockitoTest.kt +++ b/tests/src/test/kotlin/test/BDDMockitoTest.kt @@ -10,7 +10,7 @@ class BDDMockitoTest { @Test fun given_will_properlyStubs() { /* Given */ - val mock = mock() + val mock = mock() /* When */ given(mock.stringResult()) will Answer { "Test" } @@ -22,7 +22,7 @@ class BDDMockitoTest { @Test fun given_willReturn_properlyStubs() { /* Given */ - val mock = mock() + val mock = mock() /* When */ given(mock.stringResult()).willReturn("Test") @@ -34,7 +34,7 @@ class BDDMockitoTest { @Test fun givenLambda_willReturn_properlyStubs() { /* Given */ - val mock = mock() + val mock = mock() /* When */ given { mock.stringResult() }.willReturn("Test") @@ -46,7 +46,7 @@ class BDDMockitoTest { @Test fun given_willReturnLambda_properlyStubs() { /* Given */ - val mock = mock() + val mock = mock() /* When */ given(mock.stringResult()).willReturn { "Test" } @@ -58,7 +58,7 @@ class BDDMockitoTest { @Test fun givenLambda_willReturnLambda_properlyStubs() { /* Given */ - val mock = mock() + val mock = mock() /* When */ given { mock.stringResult() } willReturn { "Test" } @@ -70,7 +70,7 @@ class BDDMockitoTest { @Test fun given_willAnswer_properlyStubs() { /* Given */ - val mock = mock() + val mock = mock() /* When */ given(mock.stringResult()).willAnswer { "Test" } @@ -82,7 +82,7 @@ class BDDMockitoTest { @Test fun given_willAnswerInfix_properlyStubs() { /* Given */ - val mock = mock() + val mock = mock() /* When */ given(mock.stringResult()) willAnswer { "Test" } @@ -94,7 +94,7 @@ class BDDMockitoTest { @Test fun given_willAnswerInfix_withInvocationInfo_properlyStubs() { /* Given */ - val mock = mock() + val mock = mock() /* When */ given(mock.stringResult(any())) willAnswer { invocation -> @@ -109,7 +109,7 @@ class BDDMockitoTest { @Test(expected = IllegalStateException::class) fun given_willThrowInfix_properlyStubs() { /* Given */ - val mock = mock() + val mock = mock() /* When */ given(mock.stringResult()) willThrow { IllegalStateException() } @@ -119,13 +119,13 @@ class BDDMockitoTest { @Test fun then() { /* Given */ - val mock = mock() + val mock = mock() whenever(mock.stringResult()).thenReturn("Test") /* When */ mock.stringResult() /* Then */ - org.mockito.kotlin.then(mock).should().stringResult() + then(mock).should().stringResult() } } diff --git a/tests/src/test/kotlin/test/Classes.kt b/tests/src/test/kotlin/test/Classes.kt index a7a9270..18894fe 100644 --- a/tests/src/test/kotlin/test/Classes.kt +++ b/tests/src/test/kotlin/test/Classes.kt @@ -42,11 +42,9 @@ open class Open { class Closed -interface Methods { - +interface SynchronousFunctions { fun closed(c: Closed) fun classClosed(c: Class) - suspend fun coroutinesClosed(c: Closed) fun closedArray(a: Array) fun closedNullableArray(a: Array) fun closedCollection(c: Collection) @@ -77,10 +75,8 @@ interface Methods { fun stringResult(): String fun stringResult(s: String): String fun nullableStringResult(): String? - fun builderMethod(): Methods + fun builderMethod(): SynchronousFunctions fun varargBooleanResult(vararg values: String): Boolean - suspend fun coroutinesClosedBooleanResult(c: Closed): Boolean - suspend fun coroutinesClassClosedBooleanResult(c: Class): Boolean fun stringArray(a: Array) fun argAndVararg(s: String, vararg a: String) @@ -91,6 +87,12 @@ interface Methods { fun nestedValueClass(v: NestedValueClass) } +interface SuspendFunctions { + suspend fun closed(c: Closed) + suspend fun closedBooleanResult(c: Closed): Boolean + suspend fun classClosedBooleanResult(c: Class): Boolean +} + @JvmInline value class ValueClass(private val content: String) diff --git a/tests/src/test/kotlin/test/MatchersTest.kt b/tests/src/test/kotlin/test/MatchersTest.kt index 9fe1eee..c913e4b 100644 --- a/tests/src/test/kotlin/test/MatchersTest.kt +++ b/tests/src/test/kotlin/test/MatchersTest.kt @@ -17,7 +17,7 @@ class MatchersTest : TestBase() { class AnyMatchersTest { @Test fun anyString() { - mock().apply { + mock().apply { string("") verify(this).string(any()) } @@ -25,7 +25,7 @@ class MatchersTest : TestBase() { @Test fun anyNullableString() { - mock().apply { + mock().apply { nullableString("") verify(this).nullableString(any()) } @@ -33,7 +33,7 @@ class MatchersTest : TestBase() { @Test fun anyBoolean() { - mock().apply { + mock().apply { boolean(true) verify(this).boolean(any()) } @@ -41,7 +41,7 @@ class MatchersTest : TestBase() { @Test fun anyBooleanArray() { - mock().apply { + mock().apply { booleanArray(booleanArrayOf(true, false, false)) verify(this).booleanArray(any()) } @@ -49,7 +49,7 @@ class MatchersTest : TestBase() { @Test fun anyChar() { - mock().apply { + mock().apply { char('3') verify(this).char(any()) } @@ -57,7 +57,7 @@ class MatchersTest : TestBase() { @Test fun anyCharArray() { - mock().apply { + mock().apply { charArray(charArrayOf('3', '4', '5')) verify(this).charArray(any()) } @@ -65,7 +65,7 @@ class MatchersTest : TestBase() { @Test fun anyByte() { - mock().apply { + mock().apply { byte(3) verify(this).byte(any()) } @@ -73,7 +73,7 @@ class MatchersTest : TestBase() { @Test fun anyByteArray() { - mock().apply { + mock().apply { byteArray(byteArrayOf(3, 4, 5)) verify(this).byteArray(any()) } @@ -81,7 +81,7 @@ class MatchersTest : TestBase() { @Test fun anyShort() { - mock().apply { + mock().apply { short(3) verify(this).short(any()) } @@ -89,7 +89,7 @@ class MatchersTest : TestBase() { @Test fun anyShortArray() { - mock().apply { + mock().apply { shortArray(shortArrayOf(3, 4, 5)) verify(this).shortArray(any()) } @@ -97,7 +97,7 @@ class MatchersTest : TestBase() { @Test fun anyInt() { - mock().apply { + mock().apply { int(3) verify(this).int(any()) } @@ -105,7 +105,7 @@ class MatchersTest : TestBase() { @Test fun anyIntArray() { - mock().apply { + mock().apply { intArray(intArrayOf(3, 4, 5)) verify(this).intArray(any()) } @@ -113,7 +113,7 @@ class MatchersTest : TestBase() { @Test fun anyLong() { - mock().apply { + mock().apply { long(3) verify(this).long(any()) } @@ -121,7 +121,7 @@ class MatchersTest : TestBase() { @Test fun anyLongArray() { - mock().apply { + mock().apply { longArray(longArrayOf(3L, 4L, 5L)) verify(this).longArray(any()) } @@ -129,7 +129,7 @@ class MatchersTest : TestBase() { @Test fun anyFloat() { - mock().apply { + mock().apply { float(3f) verify(this).float(any()) } @@ -137,7 +137,7 @@ class MatchersTest : TestBase() { @Test fun anyFloatArray() { - mock().apply { + mock().apply { floatArray(floatArrayOf(3f, 4f, 5f)) verify(this).floatArray(any()) } @@ -145,7 +145,7 @@ class MatchersTest : TestBase() { @Test fun anyDouble() { - mock().apply { + mock().apply { double(3.0) verify(this).double(any()) } @@ -153,7 +153,7 @@ class MatchersTest : TestBase() { @Test fun anyDoubleArray() { - mock().apply { + mock().apply { doubleArray(doubleArrayOf(3.0, 4.0, 5.0)) verify(this).doubleArray(any()) } @@ -161,7 +161,7 @@ class MatchersTest : TestBase() { @Test fun anyClosedClass() { - mock().apply { + mock().apply { closed(Closed()) verify(this).closed(any()) } @@ -169,26 +169,25 @@ class MatchersTest : TestBase() { @Test fun anyClassClosedClass() { - mock().apply { + mock().apply { classClosed(Closed::class.java) verify(this).classClosed(any()) } } @Test - fun anyCoroutinesClosedClass() { - mock().apply { - runTest { - coroutinesClosed(Closed()) - verify(this@apply).coroutinesClosed(any()) - } - } + fun anySuspendFunctionsClosedClass() = runTest { + val mock = mock() + + mock.closed(Closed()) + + verify(mock).closed(any()) } /** https://github.com/nhaarman/mockito-kotlin/issues/27 */ @Test fun anyThrowableWithSingleThrowableConstructor() { - mock().apply { + mock().apply { throwableClass(ThrowableClass(IOException())) verify(this).throwableClass(any()) } @@ -196,7 +195,7 @@ class MatchersTest : TestBase() { @Test fun anyValueClass() { - mock().apply { + mock().apply { valueClass(ValueClass("Content")) verify(this).valueClass(any()) } @@ -204,7 +203,7 @@ class MatchersTest : TestBase() { @Test fun anyNeverVerifiesForNullValue() { - mock().apply { + mock().apply { nullableString(null) verify(this, never()).nullableString(any()) } @@ -214,7 +213,7 @@ class MatchersTest : TestBase() { class SpecialAnyMatchersTest { @Test fun anyClassArray() { - mock().apply { + mock().apply { closedArray(arrayOf(Closed())) verify(this).closedArray(anyArray()) } @@ -222,7 +221,7 @@ class MatchersTest : TestBase() { @Test fun anyNullableClassArray() { - mock().apply { + mock().apply { closedNullableArray(arrayOf(Closed(), null)) verify(this).closedNullableArray(anyArray()) } @@ -230,7 +229,7 @@ class MatchersTest : TestBase() { @Test fun anyStringVararg() { - mock().apply { + mock().apply { closedVararg(Closed(), Closed()) verify(this).closedVararg(anyVararg()) } @@ -238,7 +237,7 @@ class MatchersTest : TestBase() { @Test fun anyVarargMatching() { - mock().apply { + mock().apply { whenever(varargBooleanResult(anyVararg())).thenReturn(true) expect(varargBooleanResult()).toBe(true) } @@ -246,7 +245,7 @@ class MatchersTest : TestBase() { @Test fun anyValueClass_withValueClass() { - mock().apply { + mock().apply { valueClass(ValueClass("Content")) verify(this).valueClass(anyValueClass()) } @@ -255,7 +254,7 @@ class MatchersTest : TestBase() { @Test fun anyValueClass_withNonValueClass() { expectErrorWithMessage("kotlin.Float is not a value class.") on { - mock().apply { + mock().apply { float(10f) // Should throw an error because Float is not a value class float(anyValueClass()) @@ -265,7 +264,7 @@ class MatchersTest : TestBase() { @Test fun anyValueClass_withNestedValueClass() { - mock().apply { + mock().apply { nestedValueClass(NestedValueClass(ValueClass("Content"))) verify(this).nestedValueClass(anyValueClass()) } @@ -275,7 +274,7 @@ class MatchersTest : TestBase() { class AnyOrNullMatchersTest { @Test fun anyOrNullString() { - mock().apply { + mock().apply { string("") verify(this).string(anyOrNull()) } @@ -283,7 +282,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullNullableString() { - mock().apply { + mock().apply { nullableString("") verify(this).nullableString(anyOrNull()) } @@ -291,7 +290,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullNullableStringNullValue() { - mock().apply { + mock().apply { nullableString(null) verify(this).nullableString(anyOrNull()) } @@ -299,7 +298,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullBoolean() { - mock().apply { + mock().apply { boolean(false) verify(this).boolean(anyOrNull()) } @@ -307,7 +306,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullByte() { - mock().apply { + mock().apply { byte(3) verify(this).byte(anyOrNull()) } @@ -315,7 +314,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullChar() { - mock().apply { + mock().apply { char('a') verify(this).char(anyOrNull()) } @@ -323,7 +322,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullShort() { - mock().apply { + mock().apply { short(3) verify(this).short(anyOrNull()) } @@ -331,7 +330,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullInt() { - mock().apply { + mock().apply { int(3) verify(this).int(anyOrNull()) } @@ -339,7 +338,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullLong() { - mock().apply { + mock().apply { long(3) verify(this).long(anyOrNull()) } @@ -347,7 +346,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullFloat() { - mock().apply { + mock().apply { float(3f) verify(this).float(anyOrNull()) } @@ -355,7 +354,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullDouble() { - mock().apply { + mock().apply { double(3.0) verify(this).double(anyOrNull()) } @@ -363,7 +362,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullValueClass() { - mock().apply { + mock().apply { valueClass(ValueClass("Content")) verify(this).valueClass(anyOrNull()) } @@ -371,7 +370,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullNullableValueClass() { - mock().apply { + mock().apply { nullableValueClass(ValueClass("Content")) verify(this).nullableValueClass(anyOrNull()) } @@ -379,7 +378,7 @@ class MatchersTest : TestBase() { @Test fun anyOrNullNullableValueClassNullValue() { - mock().apply { + mock().apply { nullableValueClass(null) verify(this).nullableValueClass(anyOrNull()) } @@ -390,7 +389,7 @@ class MatchersTest : TestBase() { @Test fun eqString() { val value = "Value" - mock().apply { + mock().apply { string(value) verify(this).string(eq(value)) } @@ -399,7 +398,7 @@ class MatchersTest : TestBase() { @Test fun eqBoolean() { val value = true - mock().apply { + mock().apply { boolean(value) verify(this).boolean(eq(value)) } @@ -408,7 +407,7 @@ class MatchersTest : TestBase() { @Test fun eqBooleanArray() { val value = booleanArrayOf(true, false, false) - mock().apply { + mock().apply { booleanArray(value) verify(this).booleanArray(eq(value)) } @@ -417,7 +416,7 @@ class MatchersTest : TestBase() { @Test fun eqChar() { val value = '3' - mock().apply { + mock().apply { char(value) verify(this).char(eq(value)) } @@ -426,7 +425,7 @@ class MatchersTest : TestBase() { @Test fun eqCharArray() { val value = charArrayOf('3', '4', '5') - mock().apply { + mock().apply { charArray(value) verify(this).charArray(eq(value)) } @@ -435,7 +434,7 @@ class MatchersTest : TestBase() { @Test fun eqByte() { val value: Byte = 3 - mock().apply { + mock().apply { byte(value) verify(this).byte(eq(value)) } @@ -444,7 +443,7 @@ class MatchersTest : TestBase() { @Test fun eqByteArray() { val value = byteArrayOf(3, 4, 5) - mock().apply { + mock().apply { byteArray(value) verify(this).byteArray(eq(value)) } @@ -453,7 +452,7 @@ class MatchersTest : TestBase() { @Test fun eqShort() { val value: Short = 3 - mock().apply { + mock().apply { short(value) verify(this).short(eq(value)) } @@ -462,7 +461,7 @@ class MatchersTest : TestBase() { @Test fun eqShortArray() { val value = shortArrayOf(3, 4, 5) - mock().apply { + mock().apply { shortArray(value) verify(this).shortArray(eq(value)) } @@ -471,7 +470,7 @@ class MatchersTest : TestBase() { @Test fun eqInt() { val value = 3 - mock().apply { + mock().apply { int(value) verify(this).int(eq(value)) } @@ -480,7 +479,7 @@ class MatchersTest : TestBase() { @Test fun eqIntArray() { val value = intArrayOf(3, 4, 5) - mock().apply { + mock().apply { intArray(value) verify(this).intArray(eq(value)) } @@ -489,7 +488,7 @@ class MatchersTest : TestBase() { @Test fun eqLong() { val value = 3L - mock().apply { + mock().apply { long(value) verify(this).long(eq(value)) } @@ -498,7 +497,7 @@ class MatchersTest : TestBase() { @Test fun eqLongArray() { val value = longArrayOf(3L, 4L, 5L) - mock().apply { + mock().apply { longArray(value) verify(this).longArray(eq(value)) } @@ -507,7 +506,7 @@ class MatchersTest : TestBase() { @Test fun eqFloat() { val value = 3f - mock().apply { + mock().apply { float(value) verify(this).float(eq(value)) } @@ -516,7 +515,7 @@ class MatchersTest : TestBase() { @Test fun eqFloatArray() { val value = floatArrayOf(3f, 4f, 5f) - mock().apply { + mock().apply { floatArray(value) verify(this).floatArray(eq(value)) } @@ -525,7 +524,7 @@ class MatchersTest : TestBase() { @Test fun eqDouble() { val value = 3.0 - mock().apply { + mock().apply { double(value) verify(this).double(eq(value)) } @@ -534,7 +533,7 @@ class MatchersTest : TestBase() { @Test fun eqDoubleArray() { val value = doubleArrayOf(3.0, 4.0, 5.0) - mock().apply { + mock().apply { doubleArray(value) verify(this).doubleArray(eq(value)) } @@ -543,7 +542,7 @@ class MatchersTest : TestBase() { @Test fun eqClosedClass() { val value = Closed() - mock().apply { + mock().apply { closed(value) verify(this).closed(eq(value)) } @@ -552,27 +551,26 @@ class MatchersTest : TestBase() { @Test fun eqClassClosedClass() { val clazz = Closed::class.java - mock().apply { + mock().apply { classClosed(clazz) verify(this).classClosed(eq(clazz)) } } @Test - fun eqCoroutinesClosedClass() { + fun eqSuspendFunctionsClosedClass() = runTest { + val mock = mock() val value = Closed() - mock().apply { - runTest { - coroutinesClosed(value) - verify(this@apply).coroutinesClosed(eq(value)) - } - } + + mock.closed(value) + + verify(mock).closed(eq(value)) } @Test fun eqClassArray() { val value = arrayOf(Closed()) - mock().apply { + mock().apply { closedArray(value) verify(this).closedArray(eq(value)) } @@ -581,7 +579,7 @@ class MatchersTest : TestBase() { @Test fun eqNullableClassArray() { val value = arrayOf(Closed(), null) - mock().apply { + mock().apply { closedNullableArray(value) verify(this).closedNullableArray(eq(value)) } @@ -590,7 +588,7 @@ class MatchersTest : TestBase() { @Test fun eqValueClass() { val valueClass = ValueClass("Content") - mock().apply { + mock().apply { valueClass(valueClass) verify(this).valueClass(eq(valueClass)) } @@ -599,7 +597,7 @@ class MatchersTest : TestBase() { @Test fun eqNestedValueClass() { val nestedValueClass = NestedValueClass(ValueClass("Content")) - mock().apply { + mock().apply { nestedValueClass(nestedValueClass) verify(this).nestedValueClass(eq(nestedValueClass)) } @@ -609,7 +607,7 @@ class MatchersTest : TestBase() { class OtherMatchersTest { @Test fun listArgThat() { - mock().apply { + mock().apply { closedList(listOf(Closed(), Closed())) verify(this).closedList( argThat { @@ -621,7 +619,7 @@ class MatchersTest : TestBase() { @Test fun listArgForWhich() { - mock().apply { + mock().apply { closedList(listOf(Closed(), Closed())) verify(this).closedList( argForWhich { @@ -633,7 +631,7 @@ class MatchersTest : TestBase() { @Test fun listArgWhere() { - mock().apply { + mock().apply { closedList(listOf(Closed(), Closed())) verify(this).closedList( argWhere { @@ -645,7 +643,7 @@ class MatchersTest : TestBase() { @Test fun listArgCheck() { - mock().apply { + mock().apply { closedList(listOf(Closed(), Closed())) verify(this).closedList( check { @@ -657,7 +655,7 @@ class MatchersTest : TestBase() { @Test fun checkProperlyFails() { - mock().apply { + mock().apply { closedList(listOf(Closed(), Closed())) expectErrorWithMessage("Argument(s) are different!") on { @@ -672,7 +670,7 @@ class MatchersTest : TestBase() { @Test fun checkWithNullArgument_throwsError() { - mock().apply { + mock().apply { nullableString(null) expectErrorWithMessage("null").on { @@ -683,7 +681,7 @@ class MatchersTest : TestBase() { @Test fun isA_withNonNullableString() { - mock().apply { + mock().apply { string("") verify(this).string(isA()) } @@ -691,7 +689,7 @@ class MatchersTest : TestBase() { @Test fun isA_withNullableString() { - mock().apply { + mock().apply { nullableString("") verify(this).nullableString(isA()) } @@ -699,7 +697,7 @@ class MatchersTest : TestBase() { @Test fun same_withNonNullArgument() { - mock().apply { + mock().apply { string("") verify(this).string(same("")) } @@ -707,7 +705,7 @@ class MatchersTest : TestBase() { @Test fun same_withNullableNonNullArgument() { - mock().apply { + mock().apply { nullableString("") verify(this).nullableString(same("")) } @@ -715,7 +713,7 @@ class MatchersTest : TestBase() { @Test fun same_withNullArgument() { - mock().apply { + mock().apply { nullableString(null) verify(this).nullableString(same(null)) } @@ -724,7 +722,7 @@ class MatchersTest : TestBase() { @Test fun testVarargAnySuccess() { /* Given */ - val t = mock() + val t = mock() // a matcher to check if any of the varargs was equals to "b" val matcher = VarargAnyMatcher({ "b" == it }, String::class.java, true, false) @@ -738,7 +736,7 @@ class MatchersTest : TestBase() { @Test fun testVarargAnyFail() { /* Given */ - val t = mock() + val t = mock() // a matcher to check if any of the varargs was equals to "d" val matcher = VarargAnyMatcher({ "d" == it }, String::class.java, true, false) @@ -752,7 +750,7 @@ class MatchersTest : TestBase() { /** https://github.com/nhaarman/mockito-kotlin/issues/328 */ @Test fun testRefEqForNonNullableParameter() { - mock().apply { + mock().apply { /* When */ val array = intArrayOf(2, 3) intArray(array) diff --git a/tests/src/test/kotlin/test/MockingTest.kt b/tests/src/test/kotlin/test/MockingTest.kt index bfcf780..2382bc8 100644 --- a/tests/src/test/kotlin/test/MockingTest.kt +++ b/tests/src/test/kotlin/test/MockingTest.kt @@ -112,7 +112,7 @@ class MockingTest : TestBase() { @Test fun mock_withCustomDefaultAnswer_parameterName() { /* Given */ - val mock = mock(defaultAnswer = Mockito.RETURNS_SELF) + val mock = mock(defaultAnswer = Mockito.RETURNS_SELF) /* When */ val result = mock.builderMethod() @@ -124,7 +124,7 @@ class MockingTest : TestBase() { @Test fun mock_withSettingsAPI_extraInterfaces() { /* Given */ - val mock = mock( + val mock = mock( extraInterfaces = arrayOf(ExtraInterface::class) ) @@ -135,7 +135,7 @@ class MockingTest : TestBase() { @Test fun mock_withSettingsAPI_name() { /* Given */ - val mock = mock(name = "myName") + val mock = mock(name = "myName") /* When */ expectErrorWithMessage("myName.stringResult()") on { @@ -146,7 +146,7 @@ class MockingTest : TestBase() { @Test fun mock_withSettingsAPI_defaultAnswer() { /* Given */ - val mock = mock(defaultAnswer = Mockito.RETURNS_MOCKS) + val mock = mock(defaultAnswer = Mockito.RETURNS_MOCKS) /* When */ val result = mock.nonDefaultReturnType() @@ -158,7 +158,7 @@ class MockingTest : TestBase() { @Test fun mock_withSettingsAPI_serializable() { /* Given */ - val mock = mock(serializable = true) + val mock = mock(serializable = true) /* Then */ expect(mock).toBeInstanceOf() @@ -167,7 +167,7 @@ class MockingTest : TestBase() { @Test fun mock_withSettingsAPI_serializableMode() { /* Given */ - val mock = mock(serializableMode = BASIC) + val mock = mock(serializableMode = BASIC) /* Then */ expect(mock).toBeInstanceOf() @@ -178,17 +178,17 @@ class MockingTest : TestBase() { /* Given */ val out = mock() System.setOut(out) - val mock = mock(verboseLogging = true) + val mock = mock(verboseLogging = true) try { /* When */ verify(mock).stringResult() fail("Expected an exception") - } catch (e: WantedButNotInvoked) { + } catch (_: WantedButNotInvoked) { /* Then */ argumentCaptor().apply { verify(out).println(capture()) - expect(lastValue.toString()).toBe("methods.stringResult();") + expect(lastValue.toString()).toBe("synchronousFunctions.stringResult();") } } } @@ -197,7 +197,7 @@ class MockingTest : TestBase() { fun mock_withSettingsAPI_invocationListeners() { /* Given */ var bool = false - val mock = mock(invocationListeners = arrayOf(InvocationListener { bool = true })) + val mock = mock(invocationListeners = arrayOf(InvocationListener { bool = true })) /* When */ mock.stringResult() @@ -209,7 +209,7 @@ class MockingTest : TestBase() { @Test fun mock_withSettingsAPI_stubOnly() { /* Given */ - val mock = mock(stubOnly = true) + val mock = mock(stubOnly = true) /* Expect */ expectErrorWithMessage("is a stubOnly() mock") on { @@ -247,7 +247,7 @@ class MockingTest : TestBase() { @Test fun mockStubbing_withSettingsAPI_extraInterfaces() { /* Given */ - val mock = mock(extraInterfaces = arrayOf(ExtraInterface::class)) {} + val mock = mock(extraInterfaces = arrayOf(ExtraInterface::class)) {} /* Then */ expect(mock).toBeInstanceOf() @@ -256,7 +256,7 @@ class MockingTest : TestBase() { @Test fun mockStubbing_withSettingsAPI_name() { /* Given */ - val mock = mock(name = "myName") {} + val mock = mock(name = "myName") {} /* When */ expectErrorWithMessage("myName.stringResult()") on { @@ -267,7 +267,7 @@ class MockingTest : TestBase() { @Test fun mockStubbing_withSettingsAPIAndStubbing_name() { /* Given */ - val mock = mock(name = "myName") { + val mock = mock(name = "myName") { on { nullableStringResult() } doReturn "foo" } @@ -279,28 +279,28 @@ class MockingTest : TestBase() { } @Test - fun mockCoroutines_withClosedBooleanReturn_name() = runTest { + fun mockSuspendFunction_withClosedBooleanReturn_name() = runTest { /* Given */ - val mock = mock(name = "myName") { - onBlocking { coroutinesClosedBooleanResult(any()) } doReturn true + val mock = mock(name = "myName") { + onBlocking { closedBooleanResult(any()) } doReturn true } /* When */ - val result = mock.coroutinesClosedBooleanResult(Closed()) + val result = mock.closedBooleanResult(Closed()) /* Then */ expect(result).toBe(true) } @Test - fun mockCoroutines_withClassClosedBooleanReturn_name() = runTest { + fun mockSuspendFunction_withClassClosedBooleanReturn_name() = runTest { /* Given */ - val mock = mock(name = "myName") { - onBlocking { coroutinesClassClosedBooleanResult(any()) } doReturn true + val mock = mock(name = "myName") { + onBlocking { classClosedBooleanResult(any()) } doReturn true } /* When */ - val result = mock.coroutinesClassClosedBooleanResult(Closed::class.java) + val result = mock.classClosedBooleanResult(Closed::class.java) /* Then */ expect(result).toBe(true) @@ -309,7 +309,7 @@ class MockingTest : TestBase() { @Test fun mockStubbing_withSettingsAPI_defaultAnswer() { /* Given */ - val mock = mock(defaultAnswer = Mockito.RETURNS_MOCKS) {} + val mock = mock(defaultAnswer = Mockito.RETURNS_MOCKS) {} /* When */ val result = mock.nonDefaultReturnType() @@ -321,7 +321,7 @@ class MockingTest : TestBase() { @Test fun mockStubbing_withSettingsAPI_serializable() { /* Given */ - val mock = mock(serializable = true) {} + val mock = mock(serializable = true) {} /* Then */ expect(mock).toBeInstanceOf() @@ -330,7 +330,7 @@ class MockingTest : TestBase() { @Test fun mockStubbing_withSettingsAPI_serializableMode() { /* Given */ - val mock = mock(serializableMode = BASIC) {} + val mock = mock(serializableMode = BASIC) {} /* Then */ expect(mock).toBeInstanceOf() @@ -341,17 +341,17 @@ class MockingTest : TestBase() { /* Given */ val out = mock() System.setOut(out) - val mock = mock(verboseLogging = true) {} + val mock = mock(verboseLogging = true) {} try { /* When */ verify(mock).stringResult() fail("Expected an exception") - } catch (e: WantedButNotInvoked) { + } catch (_: WantedButNotInvoked) { /* Then */ argumentCaptor().apply { verify(out).println(capture()) - expect(lastValue.toString()).toBe("methods.stringResult();") + expect(lastValue.toString()).toBe("synchronousFunctions.stringResult();") } } } @@ -360,7 +360,7 @@ class MockingTest : TestBase() { fun mockStubbing_withSettingsAPI_invocationListeners() { /* Given */ var bool = false - val mock = mock(invocationListeners = arrayOf(InvocationListener { bool = true })) {} + val mock = mock(invocationListeners = arrayOf(InvocationListener { bool = true })) {} /* When */ mock.stringResult() @@ -372,7 +372,7 @@ class MockingTest : TestBase() { @Test fun mockStubbing_withSettingsAPI_stubOnly() { /* Given */ - val mock = mock(stubOnly = true) {} + val mock = mock(stubOnly = true) {} /* Expect */ expectErrorWithMessage("is a stubOnly() mock") on { diff --git a/tests/src/test/kotlin/test/OngoingStubbingTest.kt b/tests/src/test/kotlin/test/OngoingStubbingTest.kt index e361067..99329d6 100644 --- a/tests/src/test/kotlin/test/OngoingStubbingTest.kt +++ b/tests/src/test/kotlin/test/OngoingStubbingTest.kt @@ -32,7 +32,7 @@ class OngoingStubbingTest : TestBase() { @Test fun `should stub builder method returning mock itself`() { /* Given */ - val mock = mock { mock -> + val mock = mock { mock -> on { builderMethod() } doReturn mock } @@ -46,7 +46,7 @@ class OngoingStubbingTest : TestBase() { @Test fun `should stub function call with nullable result`() { /* Given */ - val mock = mock { + val mock = mock { on { nullableStringResult() } doReturn "Test" } @@ -60,7 +60,7 @@ class OngoingStubbingTest : TestBase() { @Test fun `should throw exception instance on function call`() { /* Given */ - val mock = mock { + val mock = mock { on { builderMethod() } doThrow IllegalArgumentException() } @@ -75,7 +75,7 @@ class OngoingStubbingTest : TestBase() { @Test fun `should throw exception class on function call`() { /* Given */ - val mock = mock { + val mock = mock { on { builderMethod() } doThrow IllegalArgumentException::class } @@ -90,7 +90,7 @@ class OngoingStubbingTest : TestBase() { @Test fun `should throw exception instances on consecutive function calls`() { /* Given */ - val mock = mock { + val mock = mock { on { builderMethod() }.doThrow( IllegalArgumentException(), UnsupportedOperationException() @@ -115,7 +115,7 @@ class OngoingStubbingTest : TestBase() { @Test fun `should throw exception classes on consecutive function calls`() { /* Given */ - val mock = mock { + val mock = mock { on { builderMethod() }.doThrow( IllegalArgumentException::class, UnsupportedOperationException::class @@ -140,7 +140,7 @@ class OngoingStubbingTest : TestBase() { @Test fun `should stub function call with result from lambda`() { /* Given */ - val mock = mock { + val mock = mock { on { stringResult() } doAnswer { "result" } } @@ -155,7 +155,7 @@ class OngoingStubbingTest : TestBase() { fun `should stub function call with result from an answer instance`() { /* Given */ /* Given */ val answer = Answer { "result" } - val mock = mock { + val mock = mock { on { stringResult() } doAnswer answer } @@ -169,7 +169,7 @@ class OngoingStubbingTest : TestBase() { @Test fun `should stub builder method returning mock itself via answer`() { /* Given */ - val mock = mock { + val mock = mock { on { builderMethod() } doAnswer Mockito.RETURNS_SELF } @@ -183,7 +183,7 @@ class OngoingStubbingTest : TestBase() { @Test fun `should stub function call with result from lambda with argument`() { /* Given */ - val mock = mock { + val mock = mock { on { stringResult(any()) } doAnswer { "${it.arguments[0]}-result" } } @@ -197,7 +197,7 @@ class OngoingStubbingTest : TestBase() { @Test fun `should stub function call with result from lambda with deconstructed argument`() { /* Given */ - val mock = mock { + val mock = mock { on { stringResult(any()) } doAnswer { (s: String) -> "$s-result" } } @@ -211,7 +211,7 @@ class OngoingStubbingTest : TestBase() { @Test fun `should stub function call with result from lambda with deconstructed arguments`() { /* Given */ - val mock = mock { + val mock = mock { on { varargBooleanResult(any(), any()) } doAnswer { (a: String, b: String) -> a == b.trim() } diff --git a/tests/src/test/kotlin/test/StubberTest.kt b/tests/src/test/kotlin/test/StubberTest.kt index 7b8f6af..5144cb7 100644 --- a/tests/src/test/kotlin/test/StubberTest.kt +++ b/tests/src/test/kotlin/test/StubberTest.kt @@ -9,7 +9,7 @@ class StubberTest : TestBase() { @Test fun testDoAnswer() { - val mock = mock() + val mock = mock() doAnswer { "Test" } .whenever(mock) @@ -41,7 +41,7 @@ class StubberTest : TestBase() { @Test fun testDoReturnValue() { - val mock = mock() + val mock = mock() doReturn("test").whenever(mock).stringResult() @@ -50,7 +50,7 @@ class StubberTest : TestBase() { @Test fun testDoReturnNullValue() { - val mock = mock() + val mock = mock() doReturn(null).whenever(mock).stringResult() @@ -59,7 +59,7 @@ class StubberTest : TestBase() { @Test fun testDoReturnNullValues() { - val mock = mock() + val mock = mock() doReturn(null, null).whenever(mock).stringResult() @@ -69,7 +69,7 @@ class StubberTest : TestBase() { @Test fun testDoReturnValues() { - val mock = mock() + val mock = mock() doReturn("test", "test2").whenever(mock).stringResult() @@ -86,7 +86,7 @@ class StubberTest : TestBase() { try { mock.go() throw AssertionError("Call should have thrown.") - } catch (e: IllegalStateException) { + } catch (_: IllegalStateException) { } } @@ -103,7 +103,7 @@ class StubberTest : TestBase() { @Test fun testStubberOnBlockExtension() { - val mock = mock { + 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 20e308f..30a159a 100644 --- a/tests/src/test/kotlin/test/StubbingTest.kt +++ b/tests/src/test/kotlin/test/StubbingTest.kt @@ -31,7 +31,7 @@ class StubbingTest { @Test fun `should stub already existing mock, using stubbing function`() { /* Given */ - val mock = mock() + val mock = mock() /* When */ stubbing(mock) { @@ -44,7 +44,7 @@ class StubbingTest { @Test fun `should stub already existing mock, using stub extension function`() { - val mock = mock() + val mock = mock() //create stub after creation of mock mock.stub { @@ -61,7 +61,7 @@ class StubbingTest { @Test fun `should override default stub of mock`() { /* Given mock with stub */ - val mock = mock { + val mock = mock { on { stringResult() } doReturn "result1" } @@ -80,7 +80,7 @@ class StubbingTest { @Test fun `should stub 2 method calls determined by ArgumentMatchers`() { /* When */ - val mock = mock { + val mock = mock { on { stringResult(argThat { this == "A" }) } doReturn "A" on { stringResult(argThat { this == "B" }) } doReturn "B" } @@ -104,7 +104,7 @@ class StubbingTest { fun `should throw when check ArgumentMatcher is applied twice`() { /* Expect */ expectErrorWithMessage("null").on { - mock { + mock { on { stringResult(check { }) } doReturn "A" on { stringResult(check { }) } doReturn "B" } diff --git a/tests/src/test/kotlin/test/VerificationTest.kt b/tests/src/test/kotlin/test/VerificationTest.kt index db5620f..62acb0c 100644 --- a/tests/src/test/kotlin/test/VerificationTest.kt +++ b/tests/src/test/kotlin/test/VerificationTest.kt @@ -10,7 +10,7 @@ class VerificationTest : TestBase() { @Test fun atLeastXInvocations() { - mock().apply { + mock().apply { string("") string("") @@ -20,7 +20,7 @@ class VerificationTest : TestBase() { @Test fun testAtLeastOnce() { - mock().apply { + mock().apply { string("") string("") @@ -30,7 +30,7 @@ class VerificationTest : TestBase() { @Test fun atMostXInvocations() { - mock().apply { + mock().apply { string("") string("") @@ -40,7 +40,7 @@ class VerificationTest : TestBase() { @Test fun testCalls() { - mock().apply { + mock().apply { string("") string("") @@ -68,7 +68,7 @@ class VerificationTest : TestBase() { @Test fun testInOrderWithReceiver() { /* Given */ - val mock = mock() + val mock = mock() /* When */ mock.string("") @@ -84,7 +84,7 @@ class VerificationTest : TestBase() { @Test fun testClearInvocations() { - val mock = mock().apply { + val mock = mock().apply { string("") } @@ -96,7 +96,7 @@ class VerificationTest : TestBase() { @Test fun testDescription() { try { - mock().apply { + mock().apply { verify(this, description("Test")).string(any()) } throw AssertionError("Verify should throw Exception.") @@ -107,7 +107,7 @@ class VerificationTest : TestBase() { @Test fun testAfter() { - mock().apply { + mock().apply { int(3) verify(this, after(10)).int(3) } From f568bda946b53984f2fb42d5095aa9592534bf0d Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Fri, 28 Nov 2025 23:04:52 +0100 Subject: [PATCH 06/10] Apply the test interfaces SynchronousFunctions and SuspendFunctions in CoroutinesTest --- tests/src/test/kotlin/test/Classes.kt | 19 +- tests/src/test/kotlin/test/CoroutinesTest.kt | 299 +++++++++---------- 2 files changed, 166 insertions(+), 152 deletions(-) diff --git a/tests/src/test/kotlin/test/Classes.kt b/tests/src/test/kotlin/test/Classes.kt index 18894fe..c9c6897 100644 --- a/tests/src/test/kotlin/test/Classes.kt +++ b/tests/src/test/kotlin/test/Classes.kt @@ -1,5 +1,9 @@ package test +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.withContext + /* * The MIT License * @@ -35,6 +39,15 @@ open class Open { } } + suspend fun intResult(i: Int): Int = + withContext(Dispatchers.Default) { i } + + suspend fun delayedIntResult(): Int = + withContext(Dispatchers.Default) { + delay(100) + 42 + } + open fun stringResult() = "Default" fun throwsNPE(): Any = throw NullPointerException("Test") @@ -71,7 +84,7 @@ interface SynchronousFunctions { fun closedVararg(vararg c: Closed) fun throwableClass(t: ThrowableClass) fun nullableString(s: String?) - + fun intResult(): Int fun stringResult(): String fun stringResult(s: String): String fun nullableStringResult(): String? @@ -91,6 +104,10 @@ interface SuspendFunctions { suspend fun closed(c: Closed) suspend fun closedBooleanResult(c: Closed): Boolean suspend fun classClosedBooleanResult(c: Class): Boolean + suspend fun intResult(): Int + suspend fun intResult(i: Int): Int + suspend fun stringResult(): String + suspend fun stringResult(s: String): String } @JvmInline diff --git a/tests/src/test/kotlin/test/CoroutinesTest.kt b/tests/src/test/kotlin/test/CoroutinesTest.kt index fa5b917..044402d 100644 --- a/tests/src/test/kotlin/test/CoroutinesTest.kt +++ b/tests/src/test/kotlin/test/CoroutinesTest.kt @@ -1,15 +1,29 @@ -@file:Suppress("EXPERIMENTAL_FEATURE_WARNING") - package test import com.nhaarman.expect.expect -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.ObsoleteCoroutinesApi +import kotlinx.coroutines.async import kotlinx.coroutines.channels.actor +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeout import org.junit.Assert.assertEquals import org.junit.Assert.assertThrows import org.junit.Test import org.mockito.InOrder -import org.mockito.kotlin.* +import org.mockito.kotlin.any +import org.mockito.kotlin.atLeastOnce +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.doSuspendableAnswer +import org.mockito.kotlin.inOrder +import org.mockito.kotlin.mock +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 { @@ -17,12 +31,12 @@ class CoroutinesTest { @Test fun stubbingSuspending() { /* Given */ - val m = mock { - onBlocking { suspending() } doReturn 42 + val m = mock { + onBlocking { intResult() } doReturn 42 } /* When */ - val result = runBlocking { m.suspending() } + val result = runBlocking { m.intResult() } /* Then */ expect(result).toBe(42) @@ -31,12 +45,12 @@ class CoroutinesTest { @Test fun stubbingSuspending_usingSuspendingFunction() { /* Given */ - val m = mock { - onBlocking { suspending() } doReturn runBlocking { SomeClass().result(42) } + val m = mock { + onBlocking { intResult() } doReturn runBlocking { Open().intResult(42) } } /* When */ - val result = runBlocking { m.suspending() } + val result = runBlocking { m.intResult() } /* Then */ expect(result).toBe(42) @@ -45,12 +59,12 @@ class CoroutinesTest { @Test fun stubbingSuspending_runBlocking() = runBlocking { /* Given */ - val m = mock { - onBlocking { suspending() } doReturn 42 + val mock = mock { + onBlocking { intResult() } doReturn 42 } /* When */ - val result = m.suspending() + val result = mock.intResult() /* Then */ expect(result).toBe(42) @@ -59,12 +73,12 @@ class CoroutinesTest { @Test fun stubbingSuspending_wheneverBlocking() { /* Given */ - val m: SomeInterface = mock() - wheneverBlocking { m.suspending() } + val mock: SuspendFunctions = mock() + wheneverBlocking { mock.intResult() } .doReturn(42) /* When */ - val result = runBlocking { m.suspending() } + val result = runBlocking { mock.intResult() } /* Then */ expect(result).toBe(42) @@ -73,14 +87,14 @@ class CoroutinesTest { @Test fun stubbingSuspending_doReturn() { /* Given */ - val m = spy(SomeClass()) + val spy = spy(Open()) doReturn(10) - .wheneverBlocking(m) { - delaying() + .wheneverBlocking(spy) { + delayedIntResult() } /* When */ - val result = runBlocking { m.delaying() } + val result = runBlocking { spy.delayedIntResult() } /* Then */ expect(result).toBe(10) @@ -89,12 +103,12 @@ class CoroutinesTest { @Test fun stubbingNonSuspending() { /* Given */ - val m = mock { - onBlocking { nonsuspending() } doReturn 42 + val mock = mock { + onBlocking { intResult() } doReturn 42 } /* When */ - val result = m.nonsuspending() + val result = mock.intResult() /* Then */ expect(result).toBe(42) @@ -103,12 +117,12 @@ class CoroutinesTest { @Test fun stubbingNonSuspending_runBlocking() = runBlocking { /* Given */ - val m = mock { - onBlocking { nonsuspending() } doReturn 42 + val mock = mock { + onBlocking { intResult() } doReturn 42 } /* When */ - val result = m.nonsuspending() + val result = mock.intResult() /* Then */ expect(result).toBe(42) @@ -117,10 +131,10 @@ class CoroutinesTest { @Test fun delayingResult() { /* Given */ - val m = SomeClass() + val instance = Open() /* When */ - val result = runBlocking { m.delaying() } + val result = runBlocking { instance.delayedIntResult() } /* Then */ expect(result).toBe(42) @@ -129,10 +143,10 @@ class CoroutinesTest { @Test fun delayingResult_runBlocking() = runBlocking { /* Given */ - val m = SomeClass() + val instance = Open() /* When */ - val result = m.delaying() + val result = instance.delayedIntResult() /* Then */ expect(result).toBe(42) @@ -141,86 +155,86 @@ class CoroutinesTest { @Test fun verifySuspendFunctionCalled() { /* Given */ - val m = mock() + val mock = mock() /* When */ - runBlocking { m.suspending() } + runBlocking { mock.intResult() } /* Then */ - runBlocking { verify(m).suspending() } + runBlocking { verify(mock).intResult() } } @Test fun verifySuspendFunctionCalled_runBlocking() = runBlocking { - val m = mock() + val mock = mock() - m.suspending() + mock.intResult() - verify(m).suspending() + verify(mock).intResult() } @Test fun verifySuspendFunctionCalled_verifyBlocking() { - val m = mock() + val mock = mock() - runBlocking { m.suspending() } + runBlocking { mock.intResult() } - verifyBlocking(m) { suspending() } + verifyBlocking(mock) { intResult() } } @Test fun verifyAtLeastOnceSuspendFunctionCalled_verifyBlocking() { - val m = mock() + val mock = mock() - runBlocking { m.suspending() } - runBlocking { m.suspending() } + runBlocking { mock.intResult() } + runBlocking { mock.intResult() } - verifyBlocking(m, atLeastOnce()) { suspending() } + verifyBlocking(mock, atLeastOnce()) { intResult() } } @Test fun verifySuspendMethod() = runBlocking { - val testSubject: SomeInterface = mock() + val mock: SuspendFunctions = mock() - testSubject.suspending() + mock.intResult() - inOrder(testSubject) { - verify(testSubject).suspending() + inOrder(mock) { + verify(mock).intResult() } } @Test fun answerWithSuspendFunction() = runBlocking { - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() - whenever(fixture.suspendingWithArg(any())).doSuspendableAnswer { - withContext(Dispatchers.Default) { it.getArgument(0) } + whenever(mock.intResult(any())).doSuspendableAnswer { + withContext(Dispatchers.Default) { it.getArgument(0) } } - assertEquals(5, fixture.suspendingWithArg(5)) + assertEquals(5, mock.intResult(5)) } @Test fun inplaceAnswerWithSuspendFunction() = runBlocking { - val fixture: SomeInterface = mock { - onBlocking { suspendingWithArg(any()) } doSuspendableAnswer { - withContext(Dispatchers.Default) { it.getArgument(0) } + val mock: SuspendFunctions = mock { + onBlocking { intResult(any()) } doSuspendableAnswer { + withContext(Dispatchers.Default) { it.getArgument(0) } } } - assertEquals(5, fixture.suspendingWithArg(5)) + assertEquals(5, mock.intResult(5)) } @Test fun callFromSuspendFunction() = runBlocking { - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() - whenever(fixture.suspendingWithArg(any())).doSuspendableAnswer { - withContext(Dispatchers.Default) { it.getArgument(0) } + whenever(mock.intResult(any())).doSuspendableAnswer { + withContext(Dispatchers.Default) { it.getArgument(0) } } val result = async { - val answer = fixture.suspendingWithArg(5) + val answer = mock.intResult(5) Result.success(answer) } @@ -231,61 +245,61 @@ class CoroutinesTest { @Test @OptIn(ObsoleteCoroutinesApi::class) fun callFromActor() = runBlocking { - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() - whenever(fixture.suspendingWithArg(any())).doSuspendableAnswer { - withContext(Dispatchers.Default) { it.getArgument(0) } + whenever(mock.intResult(any())).doSuspendableAnswer { + withContext(Dispatchers.Default) { it.getArgument(0) } } val actor = actor> { for (element in channel) { - fixture.suspendingWithArg(element.get()) + mock.intResult(element.get()) } } actor.send(Optional.of(10)) actor.close() - verify(fixture).suspendingWithArg(10) + verify(mock).intResult(10) Unit } @Test fun answerWithSuspendFunctionWithoutArgs() = runBlocking { - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() - whenever(fixture.suspending()).doSuspendableAnswer { + whenever(mock.intResult()).doSuspendableAnswer { withContext(Dispatchers.Default) { 42 } } - assertEquals(42, fixture.suspending()) + assertEquals(42, mock.intResult()) } @Test fun answerWithSuspendFunctionWithDestructuredArgs() = runBlocking { - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() - whenever(fixture.suspendingWithArg(any())).doSuspendableAnswer { (i: Int) -> + whenever(mock.intResult(any())).doSuspendableAnswer { (i: Int) -> withContext(Dispatchers.Default) { i } } - assertEquals(5, fixture.suspendingWithArg(5)) + assertEquals(5, mock.intResult(5)) } @Test fun willAnswerWithControlledSuspend() = runBlocking { - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() val job = Job() - whenever(fixture.suspending()).doSuspendableAnswer { + whenever(mock.intResult()).doSuspendableAnswer { job.join() 5 } val asyncTask = async { - fixture.suspending() + mock.intResult() } job.complete() @@ -297,25 +311,25 @@ class CoroutinesTest { @Test fun stubberAnswerWithSuspendFunction() = runBlocking { - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() doSuspendableAnswer { withContext(Dispatchers.Default) { it.getArgument(0) } - }.whenever(fixture).suspendingWithArg(any()) + }.whenever(mock).intResult(any()) - assertEquals(5, fixture.suspendingWithArg(5)) + assertEquals(5, mock.intResult(5)) } @Test fun stubberCallFromSuspendFunction() = runBlocking { - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() doSuspendableAnswer { withContext(Dispatchers.Default) { it.getArgument(0) } - }.whenever(fixture).suspendingWithArg(any()) + }.whenever(mock).intResult(any()) val result = async { - val answer = fixture.suspendingWithArg(5) + val answer = mock.intResult(5) Result.success(answer) } @@ -326,61 +340,61 @@ class CoroutinesTest { @Test @OptIn(ObsoleteCoroutinesApi::class) fun stubberCallFromActor() = runBlocking { - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() doSuspendableAnswer { withContext(Dispatchers.Default) { it.getArgument(0) } - }.whenever(fixture).suspendingWithArg(any()) + }.whenever(mock).intResult(any()) val actor = actor> { for (element in channel) { - fixture.suspendingWithArg(element.get()) + mock.intResult(element.get()) } } actor.send(Optional.of(10)) actor.close() - verify(fixture).suspendingWithArg(10) + verify(mock).intResult(10) Unit } @Test fun stubberAnswerWithSuspendFunctionWithoutArgs() = runBlocking { - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() doSuspendableAnswer { withContext(Dispatchers.Default) { 42 } - }.whenever(fixture).suspending() + }.whenever(mock).intResult() - assertEquals(42, fixture.suspending()) + assertEquals(42, mock.intResult()) } @Test fun stubberAnswerWithSuspendFunctionWithDestructuredArgs() = runBlocking { - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() doSuspendableAnswer { (i: Int) -> withContext(Dispatchers.Default) { i } - }.whenever(fixture).suspendingWithArg(any()) + }.whenever(mock).intResult(any()) - assertEquals(5, fixture.suspendingWithArg(5)) + assertEquals(5, mock.intResult(5)) } @Test fun stubberWillAnswerWithControlledSuspend() = runBlocking { - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() val job = Job() doSuspendableAnswer { job.join() 5 - }.whenever(fixture).suspending() + }.whenever(mock).intResult() val asyncTask = async { - fixture.suspending() + mock.intResult() } job.complete() @@ -393,10 +407,10 @@ class CoroutinesTest { @Test fun inOrderRemainsCompatible() { /* Given */ - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() /* When */ - val inOrder = inOrder(fixture) + val inOrder = inOrder(mock) /* Then */ expect(inOrder).toBeInstanceOf() @@ -405,77 +419,77 @@ class CoroutinesTest { @Test fun inOrderSuspendingCalls() { /* Given */ - val fixtureOne: SomeInterface = mock() - val fixtureTwo: SomeInterface = mock() + val mockOne: SuspendFunctions = mock() + val mockTwo: SuspendFunctions = mock() /* When */ runBlocking { - fixtureOne.suspending() - fixtureTwo.suspending() + mockOne.intResult() + mockTwo.intResult() } /* Then */ - val inOrder = inOrder(fixtureOne, fixtureTwo) - inOrder.verifyBlocking(fixtureOne) { suspending() } - inOrder.verifyBlocking(fixtureTwo) { suspending() } + val inOrder = inOrder(mockOne, mockTwo) + inOrder.verifyBlocking(mockOne) { intResult() } + inOrder.verifyBlocking(mockTwo) { intResult() } } @Test fun inOrderSuspendingCallsFailure() { /* Given */ - val fixtureOne: SomeInterface = mock() - val fixtureTwo: SomeInterface = mock() + val mockOne: SuspendFunctions = mock() + val mockTwo: SuspendFunctions = mock() /* When */ runBlocking { - fixtureOne.suspending() - fixtureTwo.suspending() + mockOne.intResult() + mockTwo.intResult() } /* Then */ - val inOrder = inOrder(fixtureOne, fixtureTwo) - inOrder.verifyBlocking(fixtureTwo) { suspending() } + val inOrder = inOrder(mockOne, mockTwo) + inOrder.verifyBlocking(mockTwo) { intResult() } assertThrows(AssertionError::class.java) { - inOrder.verifyBlocking(fixtureOne) { suspending() } + inOrder.verifyBlocking(mockOne) { intResult() } } } @Test fun inOrderBlockSuspendingCalls() { /* Given */ - val fixtureOne: SomeInterface = mock() - val fixtureTwo: SomeInterface = mock() + val mockOne: SuspendFunctions = mock() + val mockTwo: SuspendFunctions = mock() /* When */ runBlocking { - fixtureOne.suspending() - fixtureTwo.suspending() + mockOne.intResult() + mockTwo.intResult() } /* Then */ - inOrder(fixtureOne, fixtureTwo) { - verifyBlocking(fixtureOne) { suspending() } - verifyBlocking(fixtureTwo) { suspending() } + inOrder(mockOne, mockTwo) { + verifyBlocking(mockOne) { intResult() } + verifyBlocking(mockTwo) { intResult() } } } @Test fun inOrderBlockSuspendingCallsFailure() { /* Given */ - val fixtureOne: SomeInterface = mock() - val fixtureTwo: SomeInterface = mock() + val mockOne: SuspendFunctions = mock() + val mockTwo: SuspendFunctions = mock() /* When */ runBlocking { - fixtureOne.suspending() - fixtureTwo.suspending() + mockOne.intResult() + mockTwo.intResult() } /* Then */ - inOrder(fixtureOne, fixtureTwo) { - verifyBlocking(fixtureTwo) { suspending() } + inOrder(mockOne, mockTwo) { + verifyBlocking(mockTwo) { intResult() } assertThrows(AssertionError::class.java) { - verifyBlocking(fixtureOne) { suspending() } + verifyBlocking(mockOne) { intResult() } } } } @@ -483,55 +497,38 @@ class CoroutinesTest { @Test fun inOrderOnObjectSuspendingCalls() { /* Given */ - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() /* When */ runBlocking { - fixture.suspendingWithArg(1) - fixture.suspendingWithArg(2) + mock.intResult(1) + mock.intResult(2) } /* Then */ - fixture.inOrder { - verifyBlocking { suspendingWithArg(1) } - verifyBlocking { suspendingWithArg(2) } + mock.inOrder { + verifyBlocking { intResult(1) } + verifyBlocking { intResult(2) } } } @Test fun inOrderOnObjectSuspendingCallsFailure() { /* Given */ - val fixture: SomeInterface = mock() + val mock: SuspendFunctions = mock() /* When */ runBlocking { - fixture.suspendingWithArg(1) - fixture.suspendingWithArg(2) + mock.intResult(1) + mock.intResult(2) } /* Then */ - fixture.inOrder { - verifyBlocking { suspendingWithArg(2) } + mock.inOrder { + verifyBlocking { intResult(2) } assertThrows(AssertionError::class.java) { - verifyBlocking { suspendingWithArg(1) } + verifyBlocking { intResult(1) } } } } } - -interface SomeInterface { - - suspend fun suspending(): Int - suspend fun suspendingWithArg(arg: Int): Int - fun nonsuspending(): Int -} - -open class SomeClass { - - suspend fun result(r: Int) = withContext(Dispatchers.Default) { r } - - open suspend fun delaying() = withContext(Dispatchers.Default) { - delay(100) - 42 - } -} From 0fbe56da4f2b5d63c494361435fdb9bf32965a34 Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Fri, 28 Nov 2025 23:17:51 +0100 Subject: [PATCH 07/10] Clean up tests asserting failure states: introduce an 'assertThrows()' assertion inspired by a similar assertion in JUnit5. The new assertion function asserts an exception of the given type is thrown by the block lambda and the exception is returned for further assertions. --- tests/src/test/kotlin/test/JUnit4Utils.kt | 13 +++++++ tests/src/test/kotlin/test/MockingTest.kt | 16 ++++---- .../test/kotlin/test/OngoingStubbingTest.kt | 37 +++++-------------- 3 files changed, 30 insertions(+), 36 deletions(-) create mode 100644 tests/src/test/kotlin/test/JUnit4Utils.kt diff --git a/tests/src/test/kotlin/test/JUnit4Utils.kt b/tests/src/test/kotlin/test/JUnit4Utils.kt new file mode 100644 index 0000000..4e6b053 --- /dev/null +++ b/tests/src/test/kotlin/test/JUnit4Utils.kt @@ -0,0 +1,13 @@ +package test + +import junit.framework.AssertionFailedError + +/** Inspired by JUnit5, asserts an exception being thrown */ +inline fun assertThrows(block: () -> Unit): E { + return try { + block.invoke() + throw AssertionFailedError("No exception of type ${(E::class).simpleName} was thrown") + } catch (e: Throwable) { + e as? E ?: throw e + } +} diff --git a/tests/src/test/kotlin/test/MockingTest.kt b/tests/src/test/kotlin/test/MockingTest.kt index 2382bc8..bd75130 100644 --- a/tests/src/test/kotlin/test/MockingTest.kt +++ b/tests/src/test/kotlin/test/MockingTest.kt @@ -180,16 +180,14 @@ class MockingTest : TestBase() { System.setOut(out) val mock = mock(verboseLogging = true) - try { - /* When */ + + /* When, Then */ + assertThrows { verify(mock).stringResult() - fail("Expected an exception") - } catch (_: WantedButNotInvoked) { - /* Then */ - argumentCaptor().apply { - verify(out).println(capture()) - expect(lastValue.toString()).toBe("synchronousFunctions.stringResult();") - } + } + argumentCaptor().apply { + verify(out).println(capture()) + expect(lastValue.toString()).toBe("synchronousFunctions.stringResult();") } } diff --git a/tests/src/test/kotlin/test/OngoingStubbingTest.kt b/tests/src/test/kotlin/test/OngoingStubbingTest.kt index 99329d6..9fb16f7 100644 --- a/tests/src/test/kotlin/test/OngoingStubbingTest.kt +++ b/tests/src/test/kotlin/test/OngoingStubbingTest.kt @@ -2,7 +2,6 @@ package test import com.nhaarman.expect.expect import com.nhaarman.expect.expectErrorWithMessage -import com.nhaarman.expect.fail import org.junit.Assume.assumeFalse import org.junit.Test import org.mockito.Mockito @@ -64,11 +63,9 @@ class OngoingStubbingTest : TestBase() { on { builderMethod() } doThrow IllegalArgumentException() } - try { - /* When */ + /* When, Then */ + assertThrows { mock.builderMethod() - fail("No exception thrown") - } catch (_: IllegalArgumentException) { } } @@ -79,11 +76,9 @@ class OngoingStubbingTest : TestBase() { on { builderMethod() } doThrow IllegalArgumentException::class } - try { - /* When */ + /* When, Then */ + assertThrows { mock.builderMethod() - fail("No exception thrown") - } catch (_: IllegalArgumentException) { } } @@ -97,18 +92,12 @@ class OngoingStubbingTest : TestBase() { ) } - try { - /* When */ + /* When, Then */ + assertThrows { mock.builderMethod() - fail("No exception thrown") - } catch (_: IllegalArgumentException) { } - - try { - /* When */ + assertThrows { mock.builderMethod() - fail("No exception thrown") - } catch (_: UnsupportedOperationException) { } } @@ -122,18 +111,12 @@ class OngoingStubbingTest : TestBase() { ) } - try { - /* When */ + /* When, Then */ + assertThrows { mock.builderMethod() - fail("No exception thrown") - } catch (_: IllegalArgumentException) { } - - try { - /* When */ + assertThrows { mock.builderMethod() - fail("No exception thrown") - } catch (_: UnsupportedOperationException) { } } From 613ce9b1c2087d3027512825363a084fdee491bf Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Fri, 28 Nov 2025 23:40:39 +0100 Subject: [PATCH 08/10] Add more test cases focussing on the stubbing of suspend functions. --- tests/src/test/kotlin/test/Classes.kt | 5 +- .../test/CoroutinesOngoingStubbingTest.kt | 283 ++++++++++++++++++ tests/src/test/kotlin/test/StubbingTest.kt | 193 +++++++++++- 3 files changed, 475 insertions(+), 6 deletions(-) create mode 100644 tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt diff --git a/tests/src/test/kotlin/test/Classes.kt b/tests/src/test/kotlin/test/Classes.kt index c9c6897..92b18e6 100644 --- a/tests/src/test/kotlin/test/Classes.kt +++ b/tests/src/test/kotlin/test/Classes.kt @@ -108,10 +108,13 @@ interface SuspendFunctions { suspend fun intResult(i: Int): Int suspend fun stringResult(): String suspend fun stringResult(s: String): String + suspend fun stringResult(s1: String, s2: String): String + suspend fun nullableStringResult(): String? + suspend fun builderMethod(): SuspendFunctions } @JvmInline -value class ValueClass(private val content: String) +value class ValueClass(val content: String) @JvmInline value class NestedValueClass(val value: ValueClass) diff --git a/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt b/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt new file mode 100644 index 0000000..ce3fcb7 --- /dev/null +++ b/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt @@ -0,0 +1,283 @@ +package test + +import com.nhaarman.expect.expect +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import org.junit.Ignore +import org.junit.Test +import org.mockito.Mockito +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.doReturnConsecutively +import org.mockito.kotlin.doThrow +import org.mockito.kotlin.mock +import org.mockito.stubbing.Answer + +class CoroutinesOngoingStubbingTest { + @Test + fun `should stub suspendable function call`() { + /* Given */ + val mock = mock { + onBlocking { stringResult() } doReturn "A" + } + + /* When */ + val result = runBlocking { mock.stringResult() } + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should stub suspendable function call within a coroutine scope`() = runTest { + /* Given */ + val mock = mock { + onBlocking { stringResult() } doReturn "A" + } + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should stub consecutive suspendable function calls`() { + /* Given */ + val mock = mock { + onBlocking { stringResult() }.doReturn("A", "B", "C") + } + + /* When */ + val result = runBlocking { + (1..3).map { _ -> + mock.stringResult() + } + } + + /* Then */ + expect(result).toBe(listOf("A", "B", "C")) + } + + @Test + fun `should stub consecutive suspendable function calls by a list of answers`() { + /* Given */ + val mock = mock { + onBlocking { stringResult() } doReturnConsecutively listOf("A", "B", "C") + } + + /* When */ + val result = runBlocking { + (1..3).map { _ -> + mock.stringResult() + } + } + + /* Then */ + expect(result).toBe(listOf("A", "B", "C")) + } + + @Ignore("Default answers do not yet work for coroutines, see https://github.com/mockito/mockito-kotlin/issues/550") + @Test + fun `should stub builder method returning mock itself via defaultAnswer`() { + /* Given */ + val mock = mock(defaultAnswer = Mockito.RETURNS_SELF) + + /* When */ + val result = runBlocking { mock.builderMethod() } + + /* Then */ + expect(result).toBeTheSameAs(mock) + } + + @Ignore("Default answers do not yet work for coroutines, see https://github.com/mockito/mockito-kotlin/issues/550") + @Test + fun `should stub builder method returning mock itself via answer`() { + /* Given */ + val mock = mock { + onBlocking { builderMethod() } doAnswer Mockito.RETURNS_SELF + } + + /* When */ + val result = runBlocking { mock.builderMethod() } + + /* Then */ + expect(result).toBeTheSameAs(mock) + } + + @Test + fun `should stub builder method returning mock itself`() { + /* Given */ + val mock = mock { mock -> + onBlocking { builderMethod() } doReturn mock + } + + /* When */ + val result = runBlocking { mock.builderMethod() } + + /* Then */ + expect(result).toBe(mock) + } + + @Test + fun `should stub suspendable function call with nullable result`() { + /* Given */ + val mock = mock { + onBlocking { nullableStringResult() } doReturn "Test" + } + + /* When */ + val result = runBlocking { mock.nullableStringResult() } + + /* Then */ + expect(result).toBe("Test") + } + + @Test + fun `should throw exception instance on suspendable function call`() { + /* Given */ + val mock = mock { + onBlocking { builderMethod() } doThrow IllegalArgumentException() + } + + /* When, Then */ + runBlocking { + assertThrows { + mock.builderMethod() + } + } + } + + @Test + fun `should throw exception class on suspendable function call`() { + /* Given */ + val mock = mock { + onBlocking { builderMethod() } doThrow IllegalArgumentException::class + } + + /* When, Then */ + runBlocking { + assertThrows { + mock.builderMethod() + } + } + } + + @Test + fun `should throw exception instances on consecutive suspendable function calls`() { + /* Given */ + val mock = mock { + onBlocking { builderMethod() }.doThrow( + IllegalArgumentException(), + UnsupportedOperationException() + ) + } + + /* When, Then */ + runBlocking { + assertThrows { + mock.builderMethod() + } + assertThrows { + mock.builderMethod() + } + } + } + + @Test + fun `should throw exception classes on consecutive suspendable function calls`() { + /* Given */ + val mock = mock { + onBlocking { builderMethod() }.doThrow( + IllegalArgumentException::class, + UnsupportedOperationException::class + ) + } + + /* When, Then */ + runBlocking { + assertThrows { + mock.builderMethod() + } + assertThrows { + mock.builderMethod() + } + } + } + + @Test + fun `should stub suspendable function call with result from answer instance`() { + /* Given */ + val answer: Answer = Answer { "result" } + val mock = mock { + onBlocking { stringResult() } doAnswer answer + } + + /* When */ + + val result = runBlocking { mock.stringResult() } + + /* Then */ + expect(result).toBe("result") + } + + @Test + fun `should stub suspendable function call with result from lambda`() { + /* Given */ + val mock = mock { + onBlocking { stringResult() } doAnswer { "result" } + } + + /* When */ + val result = runBlocking { mock.stringResult() } + + /* Then */ + expect(result).toBe("result") + } + + @Test + fun `should stub suspendable function call with result from lambda with argument`() { + /* Given */ + val mock = mock { + onBlocking { stringResult(any()) } doAnswer { "${it.arguments[0]}-result" } + } + + /* When */ + val result = runBlocking { mock.stringResult("argument") } + + /* Then */ + expect(result).toBe("argument-result") + } + + @Test + 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" } + } + + /* When */ + val result = runBlocking { mock.stringResult("argument") } + + /* Then */ + expect(result).toBe("argument-result") + } + + @Test + 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) -> + "$a + $b" + } + } + + /* When */ + val result = runBlocking { mock.stringResult("apple", "banana") } + + /* Then */ + expect(result).toBe("apple + banana") + } +} diff --git a/tests/src/test/kotlin/test/StubbingTest.kt b/tests/src/test/kotlin/test/StubbingTest.kt index 30a159a..77a1c38 100644 --- a/tests/src/test/kotlin/test/StubbingTest.kt +++ b/tests/src/test/kotlin/test/StubbingTest.kt @@ -2,20 +2,26 @@ package test import com.nhaarman.expect.expect import com.nhaarman.expect.expectErrorWithMessage +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import org.junit.Test +import org.mockito.exceptions.misusing.NotAMockException import org.mockito.exceptions.misusing.UnfinishedStubbingException import org.mockito.kotlin.argThat import org.mockito.kotlin.check import org.mockito.kotlin.doReturn +import org.mockito.kotlin.doSuspendableAnswer import org.mockito.kotlin.mock import org.mockito.kotlin.stub import org.mockito.kotlin.stubbing import org.mockito.kotlin.whenever +import org.mockito.kotlin.wheneverBlocking class StubbingTest { @Test - fun `should stub function call`() { + fun `should stub sync method call as part of mock creation`() { /* Given */ val mock = mock { on { stringResult() } doReturn "A" @@ -92,12 +98,26 @@ class StubbingTest { @Test fun `should throw when trying to stub a real object with stub extension method`() { - val notAMock = "" + /* Given */ + val notAMock = Open() - /* Expect */ - expectErrorWithMessage("is not a mock!").on { - notAMock.stub { } + /* When, Then */ + val exception: NotAMockException = assertThrows { + notAMock.stub { } } + expect(exception.message).toContain("Stubbing target is not a mock!") + } + + @Test + fun `should throw when trying to stub a real object with stubbing method`() { + /* Given */ + val notAMock = Open() + + /* When, Then */ + val exception: NotAMockException = assertThrows { + stubbing(notAMock) { } + } + expect(exception.message).toContain("Stubbing target is not a mock!") } @Test @@ -126,4 +146,167 @@ class StubbingTest { 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() } + } + + expect(mock.stringResult()).toBe("A") + } + + @Test + fun `should stub sync method call, with whenever`() { + /* Given */ + val mock = mock() + whenever(mock.stringResult()).doReturn("A") + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should stub sync method call, with reverse whenever`() { + /* Given */ + val mock = mock() + doReturn("A").whenever(mock).stringResult() + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should stub sync method call, using stub extension method`() { + /* Given */ + val mock = mock() + mock.stub{ + on { stringResult() } doReturn "A" + } + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should stub sync method call, using stubbing method`() { + /* Given */ + val mock = mock() + stubbing(mock) { + on { stringResult() } doReturn "A" + } + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should stub suspendable function call, with wheneverBlocking as part of mock creation`() { + /* Given */ + val mock = mock { mock -> + wheneverBlocking { mock.stringResult() } doReturn "A" + } + + /* When */ + val result = runBlocking { mock.stringResult() } + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should stub suspendable function call in reverse manner, with wheneverBlocking as part of mock creation`() { + /* Given */ + val mock = mock { mock -> + doReturn( "A").wheneverBlocking(mock) { 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 { mock -> + doReturn( "A").whenever(mock).stringResult() + } + + /* When */ + val result = runBlocking { mock.stringResult() } + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should stub sync function call within a suspendable lambda`() { + /* Given */ + val mock = mock { + onBlocking { stringResult() } doReturn "A" + } + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should stub sync function call within a suspendable lambda of wheneverBlocking`() { + /* Given */ + val mock = mock() + wheneverBlocking { mock.stringResult() } doReturn "A" + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should stub suspend function call with a call to the sync version of whenever`() = runTest { + /* Given */ + val mock = mock() + whenever (mock.stringResult()) doReturn "A" + + /* When */ + val result = mock.stringResult() + + /* Then */ + expect(result).toBe("A") + } + + @Test + fun `should provide backwards support to stub suspendable function call with 'whenever' method`() = runTest { + /* Given */ + val mock = mock() + whenever(mock.stringResult()).doSuspendableAnswer { + delay(0) + "A" + } + + /* When */ + val result = runBlocking { mock.stringResult() } + + /* Then */ + expect(result).toBe("A") + } } From 302d9a58c8f2d421cb6ad2745926982a77e5313f Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Sat, 29 Nov 2025 14:47:21 +0100 Subject: [PATCH 09/10] Add more stubbing tests for consecutive calls and builder mocking --- .../test/kotlin/test/OngoingStubbingTest.kt | 84 +++++++++++++------ 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/tests/src/test/kotlin/test/OngoingStubbingTest.kt b/tests/src/test/kotlin/test/OngoingStubbingTest.kt index 9fb16f7..c50f080 100644 --- a/tests/src/test/kotlin/test/OngoingStubbingTest.kt +++ b/tests/src/test/kotlin/test/OngoingStubbingTest.kt @@ -28,6 +28,64 @@ class OngoingStubbingTest : TestBase() { expect(result).toBe("A") } + @Test + fun `should stub consecutive function calls`() { + /* Given */ + val mock = mock { + on { stringResult() }.doReturn ("A", "B", "C") + } + + /* When */ + val result =(1..3).map { _ -> + mock.stringResult() + } + + /* Then */ + expect(result).toBe(listOf("A", "B", "C")) + } + + @Test + fun `should stub consecutive function calls by a list of answers`() { + /* Given */ + val mock = mock { + on { stringResult() } doReturnConsecutively listOf("A", "B", "C") + } + + /* When */ + val result =(1..3).map { _ -> + mock.stringResult() + } + + /* Then */ + expect(result).toBe(listOf("A", "B", "C")) + } + + @Test + fun `should stub builder method returning mock itself via answer`() { + /* Given */ + val mock = mock { + on { builderMethod() } doAnswer Mockito.RETURNS_SELF + } + + /* When */ + val result = mock.builderMethod() + + /* Then */ + expect(result).toBe(mock) + } + + @Test + fun `should stub builder method returning mock itself via defaultAnswer`() { + /* Given */ + val mock = mock(defaultAnswer = Mockito.RETURNS_SELF) + + /* When */ + val result = mock.builderMethod() + + /* Then */ + expect(result).toBeTheSameAs(mock) + } + @Test fun `should stub builder method returning mock itself`() { /* Given */ @@ -149,20 +207,6 @@ class OngoingStubbingTest : TestBase() { expect(result).toBe("result") } - @Test - fun `should stub builder method returning mock itself via answer`() { - /* Given */ - val mock = mock { - on { builderMethod() } doAnswer Mockito.RETURNS_SELF - } - - /* When */ - val result = mock.builderMethod() - - /* Then */ - expect(result).toBe(mock) - } - @Test fun `should stub function call with result from lambda with argument`() { /* Given */ @@ -207,18 +251,6 @@ class OngoingStubbingTest : TestBase() { expect(result).toBe(true) } - @Test - fun `should stub consecutive function calls by a list of answers`() { - /* Given */ - val mock = mock { - on { stringResult() } doReturnConsecutively listOf("a", "b") - } - - /* Then */ - expect(mock.stringResult()).toBe("a") - expect(mock.stringResult()).toBe("b") - } - @Test fun `should stub function call with integer result`() { /* Given */ From d4ca2f387771c50d9484f1631735e4e055ff0fe3 Mon Sep 17 00:00:00 2001 From: Mark Koops Date: Sat, 29 Nov 2025 15:02:46 +0100 Subject: [PATCH 10/10] Add stubbing tests for sync and suspend functions returning value classes --- tests/src/test/kotlin/test/Classes.kt | 6 ++ .../test/CoroutinesOngoingStubbingTest.kt | 57 +++++++++++++++++ .../test/kotlin/test/OngoingStubbingTest.kt | 64 +++++++++++++++++++ 3 files changed, 127 insertions(+) diff --git a/tests/src/test/kotlin/test/Classes.kt b/tests/src/test/kotlin/test/Classes.kt index 92b18e6..ca06afd 100644 --- a/tests/src/test/kotlin/test/Classes.kt +++ b/tests/src/test/kotlin/test/Classes.kt @@ -98,6 +98,9 @@ interface SynchronousFunctions { fun valueClass(v: ValueClass) fun nullableValueClass(v: ValueClass?) fun nestedValueClass(v: NestedValueClass) + fun valueClassResult(): ValueClass + fun nullableValueClassResult(): ValueClass? + fun nestedValueClassResult(): NestedValueClass } interface SuspendFunctions { @@ -110,6 +113,9 @@ interface SuspendFunctions { suspend fun stringResult(s: String): String suspend fun stringResult(s1: String, s2: String): String suspend fun nullableStringResult(): String? + suspend fun valueClassResult(): ValueClass + suspend fun nullableValueClassResult(): ValueClass? + suspend fun nestedValueClassResult(): NestedValueClass suspend fun builderMethod(): SuspendFunctions } diff --git a/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt b/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt index ce3fcb7..f3ee306 100644 --- a/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt +++ b/tests/src/test/kotlin/test/CoroutinesOngoingStubbingTest.kt @@ -1,6 +1,7 @@ package test import com.nhaarman.expect.expect +import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.junit.Ignore @@ -10,6 +11,7 @@ import org.mockito.kotlin.any import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.doReturnConsecutively +import org.mockito.kotlin.doSuspendableAnswer import org.mockito.kotlin.doThrow import org.mockito.kotlin.mock import org.mockito.stubbing.Answer @@ -280,4 +282,59 @@ class CoroutinesOngoingStubbingTest { /* Then */ expect(result).toBe("apple + banana") } + + @Test + fun `should stub suspendable function call with value class result`() = runTest { + /* Given */ + val valueClass = ValueClass("A") + val mock = mock { + on(mock.valueClassResult()) doSuspendableAnswer { + delay(1) + valueClass + } + } + + /* When */ + val result: ValueClass = mock.valueClassResult() + + /* Then */ + expect(result).toBe(valueClass) + } + + @Test + fun `should stub suspendable function call with nullable value class result`() = runTest { + /* Given */ + val valueClass = ValueClass("A") + val mock = mock { + on (mock.nullableValueClassResult()) doSuspendableAnswer { + delay(1) + valueClass + } + } + + /* When */ + val result: ValueClass? = mock.nullableValueClassResult() + + /* Then */ + expect(result).toBe(valueClass) + } + + @Test + fun `should stub suspendable function call with nested value class result`() = runTest { + /* Given */ + val nestedValueClass = NestedValueClass(ValueClass("A")) + val mock = mock { + on (mock.nestedValueClassResult()) doSuspendableAnswer { + delay(1) + nestedValueClass + } + } + + /* When */ + val result: NestedValueClass = mock.nestedValueClassResult() + + /* Then */ + expect(result).toBe(nestedValueClass) + expect(result.value).toBe(nestedValueClass.value) + } } diff --git a/tests/src/test/kotlin/test/OngoingStubbingTest.kt b/tests/src/test/kotlin/test/OngoingStubbingTest.kt index c50f080..0732fcc 100644 --- a/tests/src/test/kotlin/test/OngoingStubbingTest.kt +++ b/tests/src/test/kotlin/test/OngoingStubbingTest.kt @@ -271,6 +271,70 @@ class OngoingStubbingTest : TestBase() { expect(m.nullableReturnType()).toBe("Test") } + @Test + fun `should stub function call with value class result`() { + /* Given */ + val valueClass = ValueClass("A") + val mock = mock { + on { valueClassResult() } doReturn valueClass + } + + /* When */ + val result: ValueClass = mock.valueClassResult() + + /* Then */ + expect(result).toBe(valueClass) + } + + @Test + fun `should stub function call with nullable value class result`() { + /* Given */ + val valueClass = ValueClass("A") + val mock = mock { + on { nullableValueClassResult() } doReturn valueClass + } + + /* When */ + val result: ValueClass? = mock.nullableValueClassResult() + + /* Then */ + expect(result).toBe(valueClass) + } + + @Test + fun `should stub function call with nested value class result`() { + /* Given */ + val nestedValueClass = NestedValueClass(ValueClass("A")) + val mock = mock { + on { nestedValueClassResult() } doReturn nestedValueClass + } + + /* When */ + val result: NestedValueClass = mock.nestedValueClassResult() + + /* Then */ + expect(result).toBe(nestedValueClass) + expect(result.value).toBe(nestedValueClass.value) + } + + @Test + fun `should stub consecutive function calls with value class results`() { + /* Given */ + val valueClassA = ValueClass("A") + val valueClassB = ValueClass("B") + val mock = mock { + on { valueClassResult() }.doReturnConsecutively(listOf(valueClassA, valueClassB)) + } + + /* When */ + val result1 = mock.valueClassResult() + val result2 = mock.valueClassResult() + + /* Then */ + expect(result1).toBe(valueClassA) + expect(result2).toBe(valueClassB) + } + @Test fun doReturn_throwsNPE() { assumeFalse(mockMakerInlineEnabled())