Reputation: 4054
Is there any way to wait for a suspending function that is running in a scope, like what runBlocking does for its running suspending functions?
For example,
class CoroutineTestCase : CoroutineScope {
val job = Job()
var value = 1
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Unconfined
fun testFunction() {
async {
delay(2000)
value = 2
}
}
}
@Test
fun testCoroutine() = runBlocking {
val coroutineTestCase = CoroutineTestCase()
coroutineTestCase.testFunction()
assertEquals(2, coroutineTestCase.value)
}
The above test fails with value being 1 and not changed (since the launch
was not being waited to finish). If the testFunction
had been a suspending function and I ran it with runBlocking inside my unit test, everything would have worked.
I've tried with other custom dispatchers (like the one below) that can blockingly run their tasks, but no luck
class TestUiContext : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
block.run()
}
}
Upvotes: 2
Views: 10203
Reputation: 4054
Okay so I figured out what is happening. The launch is not awaited because its returned value is never used.
In the example above, the testFunction
should return the returned value of launch, which is a Deffered object that can be awaited/joined. So to actually wait for its completion, the code has to be changed as below:
class CoroutineTestCase : CoroutineScope {
val job = Job()
var value = 1
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Unconfined
fun testFunction(): Deferred<Unit> {
return async {
delay(20000)
value = 2
}
}
}
@Test
fun testCoroutine() = runBlocking {
val coroutineTestCase = CoroutineTestCase()
coroutineTestCase.testFunction().await()
assertEquals(2, coroutineTestCase.value)
}
Currently the only problem is that, in this case, it actually delays 20 seconds (with the unconfined dispatcher).
Upvotes: 3