Stephen Edwards
04/08/2020, 2:11 PMContinuation
of a suspending function to later inject values (.resume()
) on it to facilitate mocking of a similar pattern to mocking a function returning a `Single`/`Observable` with an RxRelay
that is exposed to the test?mock.`when`(suspendingFunction()).thenReturn {
suspendCoroutine<T> { continuation ->
testScopeVariable = continuation // <-- exposing this for later resumption
}
}
elizarov
04/08/2020, 3:12 PMCOROUTINE_SUSPENDED
in this case. More details on how it all works internally are here: https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md#implementation-detailswasyl
04/08/2020, 3:12 PMinfix fun <T> OngoingStubbing<T>.willAnswer(block: suspend () -> T) =
doAnswer(Answer {
val rawInvocation = it as InterceptedInvocation
@Suppress("UNCHECKED_CAST") // <https://github.com/nhaarman/mockito-kotlin/issues/346>
val continuation = rawInvocation.rawArguments.last() as Continuation<T?>
suspend { block() }.startCoroutineUninterceptedOrReturn(continuation)
})
Stephen Edwards
04/08/2020, 3:13 PMwasyl
04/08/2020, 3:24 PMval subject = CompletableDeferred<Unit>()
val mock = mock<SomeInterface> {
onBlocking { suspendFunction() } willAnswer { subject.await() }
}
Just the suspended function needs to be called in a different scope than the test, otherwise you’d get a deadlockStephen Edwards
04/08/2020, 3:25 PMwasyl
04/08/2020, 3:25 PMStephen Edwards
04/08/2020, 3:26 PM