with-runTest

Example using runTest to control virtual time in tests.

Code

package com.glassthought.sandbox

import com.glassthought.sandbox.impl.CustomDescribeSpec
import com.glassthought.sandbox.util.out.impl.out
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds

/** Shows basics of using runTest so that functions like 'delay' become virtual timed. */
@OptIn(ExperimentalCoroutinesApi::class)
class VirtualTimeDescribeSpec : CustomDescribeSpec({

  describe("Timer Service") {

    it("should complete instantly with virtual time") {
      runTest {
        var done = false

        out.info("advanceUntilIdle() auto advances")
        launch {
          out.delay(5.seconds) // Would normally take 5 seconds

          done = true
        }

        // Time jumps forward instantly
        advanceUntilIdle()

        done shouldBe true
      }
    }

    it("should control time step by step") {
      runTest {
        var counter = 0

        out.info("advanceTimeBy() gives precise control.")

        launch {
          out.delay(1.seconds)
          counter++
          out.delay(1.seconds)
          counter++
        }

        advanceTimeBy(1100.milliseconds)
        counter shouldBe 1

        advanceTimeBy(1100.milliseconds)
        counter shouldBe 2
      }
    }
  }
})


private var count = 0
private suspend fun incrementCountAndPrint(string: String) {
  count += 1

  out.info("[${count}]: ${string}")
}

private suspend fun incrementAndPrintForIt(msg: String) {
  incrementCountAndPrint(msg)
  out.info("")
}

Command to reproduce:

gt.sandbox.checkout.commit 680dd5e9e988c8ebec90 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew test --rerun-tasks"

Recorded output of command:

Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
> Task :app:checkKotlinGradlePluginConfigurationErrors SKIPPED
> Task :app:processResources NO-SOURCE
> Task :app:processTestResources NO-SOURCE
> Task :app:compileKotlin
> Task :app:compileJava NO-SOURCE
> Task :app:classes UP-TO-DATE
> Task :app:compileTestKotlin
> Task :app:compileTestJava NO-SOURCE
> Task :app:testClasses UP-TO-DATE

> Task :app:test
Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug

Gradle Test Executor 1 STANDARD_OUT
    Warning: Kotest autoscan is enabled. This means Kotest will scan the classpath for extensions that are annotated with @AutoScan. To avoid this startup cost, disable autoscan by setting the system property 'kotest.framework.classpath.scanning.autoscan.disable=true'. In 6.0 this value will default to true. For further details see https://kotest.io/docs/next/framework/project-config.html#runtime-detection

com.glassthought.sandbox.VirtualTimeDescribeSpec > Timer Service > should complete instantly with virtual time STARTED

com.glassthought.sandbox.VirtualTimeDescribeSpec > Timer Service > should complete instantly with virtual time STANDARD_OUT
    [INFO][elapsed:   38ms][①][coroutname:@kotlinx.coroutines.test runner#5] advanceUntilIdle() auto advances
    [INFO][elapsed:   42ms][⓶][coroutname:@coroutine#6] [🐢] Delaying for [5s]
    [INFO][elapsed:   43ms][⓶][coroutname:@coroutine#6] Done delaying for [5s]

com.glassthought.sandbox.VirtualTimeDescribeSpec > Timer Service > should complete instantly with virtual time PASSED

com.glassthought.sandbox.VirtualTimeDescribeSpec > Timer Service > should control time step by step STARTED

com.glassthought.sandbox.VirtualTimeDescribeSpec > Timer Service > should control time step by step STANDARD_OUT
    [INFO][elapsed:   52ms][⓷][coroutname:@kotlinx.coroutines.test runner#8] advanceTimeBy() gives precise control.
    [INFO][elapsed:   52ms][⓸][coroutname:@coroutine#9] [🐢] Delaying for [1s]
    [INFO][elapsed:   52ms][⓸][coroutname:@coroutine#9] Done delaying for [1s]
    [INFO][elapsed:   52ms][⓸][coroutname:@coroutine#9] [🐢] Delaying for [1s]
    [INFO][elapsed:   52ms][⓸][coroutname:@coroutine#9] Done delaying for [1s]

com.glassthought.sandbox.VirtualTimeDescribeSpec > Timer Service > should control time step by step PASSED

BUILD SUCCESSFUL in 2s
3 actionable tasks: 3 executed

Backlinks