Eliminating Coroutine leaks in tests

Rick Busarow
ProAndroidDev
Published in
9 min readMay 24, 2019

--

Coroutines are all the rage right now, and if you’re writing coroutines, you’re probably writing tests for them. Writing tests for coroutines can be difficult, not only because of concurrency concerns, but because it is very easy to create leaks which live on past the lifecycle of an individual test.

Consider this example class:

class Subject {

var someBoolean = false

fun CoroutineScope.loop() {

someBoolean = true

launch {
repeat(10) { count ->
delay(timeMillis = 1_000)
println("loop is running -- $count")
}
println("all done")
}
}
}

This single function loop() will immediately update someBoolean, then create a new coroutine with launch { }. This coroutine will run asynchronously for 10 seconds, printing a reminder that it’s running every second. Since launch { } is non-blocking, loop() will return before the new coroutine finishes.

Now, let’s consider a simple JUnit 4 test class which may be testing that loop function.

import io.kotlintest.shouldBeclass MyLeakyTest {

val scope = CoroutineScope(Dispatchers.Unconfined)

val subject = Subject()

@Before
fun before() {
println("before my leaky test")
}

@After
fun after() {
println("after my leaky test")
}

@Test
fun…

--

--