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

Marcelo Hernandez

05/18/2019, 8:55 PM
Been looking at the new test helpers such as
runBlockingTest
,
TestCoroutineDispatcher
,
TestCoroutineScope
, etc. They look promising, however I haven't been able to figure out a way to stub a dependency's
suspend
function using
mockito-kotlin
such that it suspends indefinitely. The goal is to test scenarios within my Android
ViewModel
where
onCleared()
is called before a
suspend
function returns a result. Basically something like:
Copy code
private fun givenStatusNeverReceived() = runBlocking {
    // loadStatus() is a suspend function
    given(repository.loadStatus()).willAnswer {
        // suspend indefinitely
    }
}
l

louiscad

05/18/2019, 10:06 PM
Use MockK, it supports coroutines
👍 2
m

Marcelo Hernandez

05/18/2019, 10:20 PM
Been meaning to explore MockK for some time. Our entire codebase has been using
mockito-kotlin
with a heavy focus on the BDDMockito APIs. I was hoping to maintain consistency moving forward, however with the recent introduction of coroutines in our codebase (we've been using RxJava heavily), it looks like
mockito-kotlin
is starting to show its limitations.
l

louiscad

05/18/2019, 10:29 PM
I'd recommend to just try it at first. Also, I think you can have them side by side.
m

Marcelo Hernandez

05/19/2019, 4:32 AM
Played around with MockK for a bit and it took some getting used to. I like how strict it is by default though. However, I do miss the style of BDDMockito. But MockK's
coEvery
,
coAnswer
,
etc.
APIs make it quite easy to mock/stub
suspend
functions.
After playing around with it, I then got a few more ideas with
mockito-kotlin
and got something working, but it looks rather ugly IMO. Basically it's something like:
Copy code
private val deferredStatus = CompletableDeferred<Status>()

...

private fun TestCoroutineScope.whileAwaitingStatus() {
    given(repository.loadStatus()).willAnswer {
        var result: Status? = null
        runBlockingTest {
            result = deferredStatus.await()
        }
        return result!!
    }
}
However, even if you choose to not set a value for the
CompletableDeferred
, you have to make sure to call
cancel()
on it otherwise
runBlockingTest
throws an exception indicating that there was still an active
Job
running.