https://kotlinlang.org logo
#coroutines
Title
# coroutines
s

Stephen Edwards

04/08/2020, 2:11 PM
Is there a way of extracting the
Continuation
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?
e.g.,
Copy code
mock.`when`(suspendingFunction()).thenReturn {
  suspendCoroutine<T> { continuation ->
    testScopeVariable = continuation  // <-- exposing this for later resumption 
  }
}
this with a Mockito paradigm not MockK
The mockito-kotlin style calling mocking mechanism for suspend functions can be inferred from this work here - https://github.com/nhaarman/mockito-kotlin/pull/357/
the problem I am having is that the call of the mocked suspending function is still returning null not suspending waiting for my resumption of the exposed continuation
the key here is I want to control when in the test the mocked coroutine resumes with it's value. I don't want it to be a list of values that can be returned immediately whenever the mock is called, but rather something I can trigger in the test
@elizarov I know you helped with the mockito-kotlin attempt by neworld - any ideas here?
e

elizarov

04/08/2020, 3:12 PM
Your mocked suspending function should return
COROUTINE_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-details
w

wasyl

04/08/2020, 3:12 PM
We have this in our project and I believe it works as expected
Copy code
infix 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)
    })
s

Stephen Edwards

04/08/2020, 3:13 PM
Thanks I will read and try that
@wasyl do you expose the continuation in a manner similar to the pseudo-code I wrote above?
w

wasyl

04/08/2020, 3:24 PM
Something like
Copy code
val 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 deadlock
(btw we found it makes more sense to not push values to coroutines like this, just mock them normally)
s

Stephen Edwards

04/08/2020, 3:25 PM
thanks for the example and wisdom. Do you use time control then to manage the timing rather than immediate return? i.e. say you were testing some loading state
w

wasyl

04/08/2020, 3:25 PM
Ah yes, loading state is exactly what this is useful for 😄
s

Stephen Edwards

04/08/2020, 3:26 PM
otherwise yes I get that direct value mocks will be preferred 🙂
38 Views