Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions mockito-kotlin/src/main/kotlin/org/mockito/kotlin/BDDMockito.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@

package org.mockito.kotlin

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
import org.mockito.BDDMockito
import org.mockito.BDDMockito.BDDMyOngoingStubbing
import org.mockito.BDDMockito.Then
import org.mockito.invocation.InvocationOnMock
import org.mockito.kotlin.internal.SuspendableAnswer
import org.mockito.stubbing.Answer
Expand All @@ -46,13 +49,31 @@ fun <T> given(methodCall: () -> T): BDDMyOngoingStubbing<T> {
return given(methodCall())
}

/**
* Alias for [BDDMockito.given] with a suspending lambda
*
* Warning: Only last method call can be stubbed in the function.
* other method calls are ignored!
*/
fun <T> givenBlocking(methodCall: suspend CoroutineScope.() -> T): BDDMockito.BDDMyOngoingStubbing<T> {
return runBlocking { BDDMockito.given(methodCall()) }
}

/**
* Alias for [BDDMockito.then].
*/
fun <T> then(mock: T): BDDMockito.Then<T> {
return BDDMockito.then(mock)
}

/**
* Alias for [Then.should], with suspending lambda.
*/
fun <T, R> Then<T>.shouldBlocking(f: suspend T.() -> R): R {
val m = should()
return runBlocking { m.f() }
}

/**
* Alias for [BDDMyOngoingStubbing.will]
* */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

package org.mockito.kotlin

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
import org.mockito.Mockito
import org.mockito.invocation.InvocationOnMock
import org.mockito.kotlin.internal.SuspendableAnswer
Expand All @@ -43,6 +45,16 @@ inline fun <T> whenever(methodCall: T): OngoingStubbing<T> {
return Mockito.`when`(methodCall)!!
}

/**
* Enables stubbing suspending methods. Use it when you want the mock to return particular value when particular suspending method is called.
*
* Warning: Only one method call can be stubbed in the function.
* other method calls are ignored!
*/
fun <T> wheneverBlocking(methodCall: suspend CoroutineScope.() -> T): OngoingStubbing<T> {
return runBlocking { Mockito.`when`(methodCall()) }
}

/**
* Sets a return value to be returned when the method is called.
*
Expand Down
14 changes: 13 additions & 1 deletion mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

package org.mockito.kotlin

import kotlinx.coroutines.runBlocking
import org.mockito.Mockito
import org.mockito.invocation.InvocationOnMock
import org.mockito.stubbing.Stubber
Expand Down Expand Up @@ -62,4 +63,15 @@ fun doThrow(vararg toBeThrown: Throwable): Stubber {
return Mockito.doThrow(*toBeThrown)!!
}

fun <T> Stubber.whenever(mock: T) = `when`(mock)
fun <T> Stubber.whenever(mock: T) = `when`(mock)

/**
* Alias for when with suspending function
*
* Warning: Only one method call can be stubbed in the function.
* Subsequent method calls are ignored!
*/
fun <T> Stubber.wheneverBlocking(mock: T, f: suspend T.() -> Unit) {
val m = whenever(mock)
runBlocking { m.f() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class BDDMockitoKtTest {
}

@Test
fun willSuspendableAnswer_witArgument() = runBlocking {
fun willSuspendableAnswer_withArgument() = runBlocking {
val fixture: SomeInterface = mock()

given(fixture.suspendingWithArg(any())).willSuspendableAnswer {
Expand All @@ -35,6 +35,40 @@ class BDDMockitoKtTest {
Unit
}

@Test
fun willSuspendableAnswer_givenBlocking() {
val fixture: SomeInterface = mock()

givenBlocking { fixture.suspending() }.willSuspendableAnswer {
withContext(Dispatchers.Default) { 42 }
}

val result = runBlocking {
fixture.suspending()
}

assertEquals(42, result)
then(fixture).shouldBlocking { suspending() }
Unit
}

@Test
fun willSuspendableAnswer_givenBlocking_withArgument() {
val fixture: SomeInterface = mock()

givenBlocking { fixture.suspendingWithArg(any()) }.willSuspendableAnswer {
withContext(Dispatchers.Default) { it.getArgument<Int>(0) }
}

val result = runBlocking {
fixture.suspendingWithArg(42)
}

assertEquals(42, result)
then(fixture).shouldBlocking { suspendingWithArg(42) }
Unit
}

@Test
fun willThrow_kclass_single() {
val fixture: SomeInterface = mock()
Expand Down
34 changes: 32 additions & 2 deletions mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,36 @@ class CoroutinesTest {
expect(result).toBe(42)
}

@Test
fun stubbingSuspending_wheneverBlocking() {
/* Given */
val m: SomeInterface = mock()
wheneverBlocking { m.suspending() }
.doReturn(42)

/* When */
val result = runBlocking { m.suspending() }

/* Then */
expect(result).toBe(42)
}

@Test
fun stubbingSuspending_doReturn() {
/* Given */
val m = spy(SomeClass())
doReturn(10)
.wheneverBlocking(m) {
delaying()
}

/* When */
val result = runBlocking { m.delaying() }

/* Then */
expect(result).toBe(10)
}

@Test
fun stubbingNonSuspending() {
/* Given */
Expand Down Expand Up @@ -394,11 +424,11 @@ interface SomeInterface {
fun nonsuspending(): Int
}

class SomeClass {
open class SomeClass {

suspend fun result(r: Int) = withContext(Dispatchers.Default) { r }

suspend fun delaying() = withContext(Dispatchers.Default) {
open suspend fun delaying() = withContext(Dispatchers.Default) {
delay(100)
42
}
Expand Down