mbonnet
12/07/2018, 2:49 PMrunBlocking
and CoroutineScopes. I want to launch a coroutine in a scope with a combined Job
for this coroutine to run until the job is cancelled. It works fine, but I would like to wrap it in a runBlocking
(for unit tests purposes), but unfortunately the runBlocking
doesn’t wait for the inner launch... Do you have any idea why ? Or how could I make it work somehow ?streetsofboston
12/07/2018, 3:57 PMlaunch
, which should return immediately….
But the first one never continue and your test hangs (it never gets to line number 11).class ExampleUnitTest {
private val testCoroutineContext = TestCoroutineContext()
private val scope : CoroutineScope = object: CoroutineScope {
override val coroutineContext: CoroutineContext = Job() + testCoroutineContext
}
@Test
fun testChannel() {
val channel = Channel<Int>()
runBlocking {
scope.launch {
for (i in channel) {
println("A: $i")
}
}
}
val job = Job()
runBlocking {
(scope + job).launch {
for (i in channel) {
println("B: $i")
}
}
}
scope.launch {
for (i in 0 .. 10) {
channel.send(i)
if (i == 5) job.cancel()
}
}
testCoroutineContext.triggerActions()
}
}
TestCoroutineContext
, you can remove any `runBlocking`:
class ExampleUnitTest : CoroutineScope {
private val testCoroutineContext = TestCoroutineContext()
override val coroutineContext: CoroutineContext = Job() + testCoroutineContext
@Test
fun testChannel() {
val channel = Channel<Int>()
launch {
for (i in channel) {
println("A: $i")
}
}
val job = Job()
(this + job).launch {
for (i in channel) {
println("B: $i")
}
}
launch {
for (i in 0..10) {
channel.send(i)
if (i == 5) job.cancel()
}
}
testCoroutineContext.triggerActions()
}
}
mbonnet
12/10/2018, 10:34 AMrunBlocking
blocks to interrupt the thread until the job was cancelled. This way, if the job is not cancelled, the unit test would run forever. But your examples helped me find a way to solve this issue : I now launch the channel consuming coroutine outside of runBlocking
, capture the associated job, and wait for its completion inside the runBlocking
block.val channel = Channel<Int>()
val job = Job()
val scope: CoroutineScope = object : CoroutineScope {
override val coroutineContext: CoroutineContext = job + Dispatchers.Default
}
val consumingJob = scope.launch {
for (i in channel) {
println("$i")
}
}
runBlocking {
delay(1_000)
job.cancel()
consumingJob.join()
}