Testing Coroutines
The kotlinx-coroutines-test library makes coroutine code fast and
deterministic to test by replacing real time with virtual time.
runTest
Wrap a suspend test body in runTest. Calls to delay are skipped instantly —
a test that “waits” 10 seconds finishes in milliseconds:
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.delay
import kotlin.test.assertEquals
@Test
fun fetchesUser() = runTest {
val user = fetchUser() // any delay() inside is auto-advanced
assertEquals("Ada", user)
}
Add the dependency:
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0")
Virtual time control
Inside runTest you have a TestScope with a scheduler you can advance
manually:
@Test
fun advancesTime() = runTest {
var done = false
launch {
delay(1000)
done = true
}
assertEquals(false, done)
advanceTimeBy(1001) // move virtual time forward
advanceUntilIdle() // run everything pending
assertEquals(true, done)
}
Injecting dispatchers
Production code should not hard-code dispatchers — inject them so tests can swap in a test dispatcher:
class UserRepository(private val io: CoroutineDispatcher = Dispatchers.IO) {
suspend fun load(): String = withContext(io) { /* ... */ "Ada" }
}
@Test
fun loads() = runTest {
val repo = UserRepository(StandardTestDispatcher(testScheduler))
assertEquals("Ada", repo.load())
}
This dependency-injection pattern is what makes ViewModels testable in Part 8.
Testing flows
Collect a flow into a list inside runTest:
@Test
fun emitsThreeValues() = runTest {
val values = numbers().toList()
assertEquals(listOf(1, 2, 3), values)
}
Exercises
-
Write a
runTestthat calls asuspendfunction containingdelay(5000)and verify it completes essentially instantly. -
Test that a
Flow<Int>of1..3collects to[1, 2, 3].
This solution is in examples/core/part6/ and is tested by CI.
Previous: Channels · Next: Part 7: Android Foundations