A TestCoroutineScheduler tracks coroutine execution and virtual time during tests. Through runTest, it fast-forwards delays instead of waiting in real time, making tests that include long delays run almost instantly. section 18.5.1
Advance virtual time deliberately
In runTest, one coroutine delays for 500 milliseconds and another for one second. Calling delay(600.milliseconds) moves the virtual clock to 600 and runs only the first coroutine; another 500 milliseconds completes the second. Use runCurrent() to execute work ready at the current time, then advanceUntilIdle() to run all scheduled work, including delayed tasks. section 18.5.1
Keep these scheduler behaviors in mind when writing assertions:
- runTest uses a single-threaded test dispatcher, so child coroutines are concurrent but do not run in parallel with the test body by default. section 18.5.1
- Assertions may run before launched coroutines unless the test reaches a suspension point such as delay or yield, or explicitly advances the scheduler. section 18.5.1
Listing 18.12 Forwarding the virtual clock with delay
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testDelay() = runTest {
var x = 0
launch {
delay(500.milliseconds)
x++
}
launch {
delay(1.second)
x++
}
println(currentTime) // 0
delay(600.milliseconds)
assertEquals(1, x)
println(currentTime) // 600
delay(500.milliseconds)
assertEquals(2, x)
println(currentTime) // 1100
}
Trace virtual-time advancement while delayed coroutines update shared state. section 18.5.1
Listing 18.13 Running all scheduled coroutines via advanceUntilIdle
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testDelay() = runTest {
var x = 0
launch {
x++
launch {
x++
}
}
launch {
delay(200.milliseconds)
x++
}
runCurrent()
assertEquals(2, x)
advanceUntilIdle()
assertEquals(3, x)
}
Compare runCurrent with advanceUntilIdle when nested and delayed coroutines are scheduled. section 18.5.1
