liveBook · Manning

Virtual time

As explained in Kotlin in Action, 2E

Virtual time is a test-controlled representation of elapsed time that allows time-dependent operations to advance without waiting for real time to pass.

Virtual time makes coroutine tests fast by replacing real waiting with a test-controlled clock. Under runTest, delays are automatically fast-forwarded, so behavior that includes long debounce or timeout delays can be checked in milliseconds rather than making the suite wait in real time. section 18.5 section 18.5.1

Fast-forward a delayed coroutine

Use runTest as the test body. A delay of 20 seconds is scheduled on its test dispatcher rather than slept in real time, so the assertion or subsequent code can run almost immediately. The default runTest timeout remains 60 real-time seconds; pass a timeout when an integration-style test genuinely needs longer. section 18.5.1

Virtual time controls scheduled coroutine work, not parallel execution. runTest uses a single-threaded dispatcher by default: launched children run concurrently but need a suspension point before they can make progress alongside test code. Calling an assertion immediately after launch can therefore observe unchanged state. Use delay, yield, or another suspending call when the test must let shared-dispatcher work run. section 18.5.1

Advance and inspect scheduled work deliberately when a test needs intermediate states:

  • Use delay inside runTest to move the virtual clock forward by a chosen duration; currentTime reports the resulting virtual time. section 18.5.1
  • Use runCurrent() to execute work scheduled at the current virtual time without advancing the clock. section 18.6
  • Use advanceUntilIdle() to run scheduled coroutines until no work remains, including later delayed work. 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
}

This timeline-style test shows two delayed coroutines becoming observable at 600 ms and 1100 ms of virtual time, without waiting for either interval in real time. section 18.5.1

Treat virtual time as a way to test time-dependent coroutine behavior deterministically and quickly. It is especially useful when repeated delays would otherwise make a growing test suite cumbersome to run, reducing the incentive to run tests frequently. section 18.5 section 18.6

How Kotlin in Action, 2E covers it

How deep it goes
Explains automatic delay forwarding, single-threaded scheduling, virtual-clock inspection, and runCurrent or advanceUntilIdle controls.
Examples
Demonstrates a 20-second delay, staged 500-millisecond and one-second tasks, and nested launched coroutines.
What it assumes
Assumes familiarity with coroutine suspension points while showing how they let launched work progress in runTest.
What you can do after
Apply it via runTest and scheduler controls to test delayed coroutine work quickly and at chosen virtual times.
How it connects
Connects virtual time with Kotlin coroutines, runTest, test dispatchers, TestCoroutineScheduler, suspension points, and flow testing.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Kotlin in Action, 2E. The section links point to the parts of the book it draws on.