Skip to content

Commit cb2c8c0

Browse files
author
Andrei Badea
committed
Add stubber (top-level) doSuspendableAnswer()
1 parent 888e19d commit cb2c8c0

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-0
lines changed

mockito-kotlin/src/main/kotlin/org/mockito/kotlin/Stubber.kt

+6
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,19 @@ package org.mockito.kotlin
2828
import kotlinx.coroutines.runBlocking
2929
import org.mockito.Mockito
3030
import org.mockito.invocation.InvocationOnMock
31+
import org.mockito.kotlin.internal.SuspendableAnswer
32+
import org.mockito.stubbing.OngoingStubbing
3133
import org.mockito.stubbing.Stubber
3234
import kotlin.reflect.KClass
3335

3436
fun <T> doAnswer(answer: (InvocationOnMock) -> T?): Stubber {
3537
return Mockito.doAnswer { answer(it) }!!
3638
}
3739

40+
fun <T> doSuspendableAnswer(answer: suspend (KInvocationOnMock) -> T?): Stubber {
41+
return Mockito.doAnswer(SuspendableAnswer(answer))
42+
}
43+
3844
fun doCallRealMethod(): Stubber {
3945
return Mockito.doCallRealMethod()!!
4046
}

mockito-kotlin/src/test/kotlin/test/CoroutinesTest.kt

+94
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,100 @@ class CoroutinesTest {
294294
}
295295
}
296296

297+
@Test
298+
fun stubberAnswerWithSuspendFunction() = runBlocking {
299+
val fixture: SomeInterface = mock()
300+
301+
doSuspendableAnswer {
302+
withContext(Dispatchers.Default) { it.getArgument<Int>(0) }
303+
}.whenever(fixture).suspendingWithArg(any())
304+
305+
assertEquals(5, fixture.suspendingWithArg(5))
306+
}
307+
308+
@Test
309+
fun stubberCallFromSuspendFunction() = runBlocking {
310+
val fixture: SomeInterface = mock()
311+
312+
doSuspendableAnswer {
313+
withContext(Dispatchers.Default) { it.getArgument<Int>(0) }
314+
}.whenever(fixture).suspendingWithArg(any())
315+
316+
val result = async {
317+
val answer = fixture.suspendingWithArg(5)
318+
319+
Result.success(answer)
320+
}
321+
322+
assertEquals(5, result.await().getOrThrow())
323+
}
324+
325+
@Test
326+
fun stubberCallFromActor() = runBlocking {
327+
val fixture: SomeInterface = mock()
328+
329+
doSuspendableAnswer {
330+
withContext(Dispatchers.Default) { it.getArgument<Int>(0) }
331+
}.whenever(fixture).suspendingWithArg(any())
332+
333+
val actor = actor<Optional<Int>> {
334+
for (element in channel) {
335+
fixture.suspendingWithArg(element.get())
336+
}
337+
}
338+
339+
actor.send(Optional.of(10))
340+
actor.close()
341+
342+
verify(fixture).suspendingWithArg(10)
343+
344+
Unit
345+
}
346+
347+
@Test
348+
fun stubberAnswerWithSuspendFunctionWithoutArgs() = runBlocking {
349+
val fixture: SomeInterface = mock()
350+
351+
doSuspendableAnswer {
352+
withContext(Dispatchers.Default) { 42 }
353+
}.whenever(fixture).suspending()
354+
355+
assertEquals(42, fixture.suspending())
356+
}
357+
358+
@Test
359+
fun stubberAnswerWithSuspendFunctionWithDestructuredArgs() = runBlocking {
360+
val fixture: SomeInterface = mock()
361+
362+
doSuspendableAnswer { (i: Int) ->
363+
withContext(Dispatchers.Default) { i }
364+
}.whenever(fixture).suspendingWithArg(any())
365+
366+
assertEquals(5, fixture.suspendingWithArg(5))
367+
}
368+
369+
@Test
370+
fun stubberWillAnswerWithControlledSuspend() = runBlocking {
371+
val fixture: SomeInterface = mock()
372+
373+
val job = Job()
374+
375+
doSuspendableAnswer {
376+
job.join()
377+
5
378+
}.whenever(fixture).suspending()
379+
380+
val asyncTask = async {
381+
fixture.suspending()
382+
}
383+
384+
job.complete()
385+
386+
withTimeout(100) {
387+
assertEquals(5, asyncTask.await())
388+
}
389+
}
390+
297391
@Test
298392
fun inOrderRemainsCompatible() {
299393
/* Given */

0 commit comments

Comments
 (0)