1# Module kotlinx-coroutines-test 2 3Test utilities for `kotlinx.coroutines`. 4 5## Overview 6 7This package provides utilities for efficiently testing coroutines. 8 9| Name | Description | 10| ---- | ----------- | 11| [runTest] | Runs the test code, automatically skipping delays and handling uncaught exceptions. | 12| [TestCoroutineScheduler] | The shared source of virtual time, used for controlling execution order and skipping delays. | 13| [TestScope] | A [CoroutineScope] that integrates with [runTest], providing access to [TestCoroutineScheduler]. | 14| [TestDispatcher] | A [CoroutineDispatcher] whose delays are controlled by a [TestCoroutineScheduler]. | 15| [Dispatchers.setMain] | Mocks the main dispatcher using the provided one. If mocked with a [TestDispatcher], its [TestCoroutineScheduler] is used everywhere by default. | 16 17Provided [TestDispatcher] implementations: 18 19| Name | Description | 20| ---- | ----------- | 21| [StandardTestDispatcher] | A simple dispatcher with no special behavior other than being linked to a [TestCoroutineScheduler]. | 22| [UnconfinedTestDispatcher] | A dispatcher that behaves like [Dispatchers.Unconfined]. | 23 24## Using in your project 25 26Add `kotlinx-coroutines-test` to your project test dependencies: 27``` 28dependencies { 29 testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1' 30} 31``` 32 33**Do not** depend on this project in your main sources, all utilities here are intended and designed to be used only from tests. 34 35## Dispatchers.Main Delegation 36 37`Dispatchers.setMain` will override the `Main` dispatcher in test scenarios. 38This is helpful when one wants to execute a test in situations where the platform `Main` dispatcher is not available, 39or to replace `Dispatchers.Main` with a testing dispatcher. 40 41On the JVM, 42the [`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) mechanism is responsible 43for overwriting [Dispatchers.Main] with a testable implementation, which by default will delegate its calls to the real 44`Main` dispatcher, if any. 45 46The `Main` implementation can be overridden using [Dispatchers.setMain][setMain] method with any [CoroutineDispatcher] 47implementation, e.g.: 48 49```kotlin 50 51class SomeTest { 52 53 private val mainThreadSurrogate = newSingleThreadContext("UI thread") 54 55 @Before 56 fun setUp() { 57 Dispatchers.setMain(mainThreadSurrogate) 58 } 59 60 @After 61 fun tearDown() { 62 Dispatchers.resetMain() // reset the main dispatcher to the original Main dispatcher 63 mainThreadSurrogate.close() 64 } 65 66 @Test 67 fun testSomeUI() = runBlocking { 68 launch(Dispatchers.Main) { // Will be launched in the mainThreadSurrogate dispatcher 69 // ... 70 } 71 } 72} 73``` 74 75Calling `setMain` or `resetMain` immediately changes the `Main` dispatcher globally. 76 77If `Main` is overridden with a [TestDispatcher], then its [TestCoroutineScheduler] is used when new [TestDispatcher] or 78[TestScope] instances are created without [TestCoroutineScheduler] being passed as an argument. 79 80## runTest 81 82[runTest] is the way to test code that involves coroutines. `suspend` functions can be called inside it. 83 84**IMPORTANT: in order to work with on Kotlin/JS, the result of `runTest` must be immediately `return`-ed from each test.** 85The typical invocation of [runTest] thus looks like this: 86 87```kotlin 88@Test 89fun testFoo() = runTest { 90 // code under test 91} 92``` 93 94In more advanced scenarios, it's possible instead to use the following form: 95```kotlin 96@Test 97fun testFoo(): TestResult { 98 // initialize some test state 99 return runTest { 100 // code under test 101 } 102} 103``` 104 105[runTest] is similar to running the code with `runBlocking` on Kotlin/JVM and Kotlin/Native, or launching a new promise 106on Kotlin/JS. The main differences are the following: 107 108* **The calls to `delay` are automatically skipped**, preserving the relative execution order of the tasks. This way, 109 it's possible to make tests finish more-or-less immediately. 110* **The execution times out after 10 seconds**, cancelling the test coroutine to prevent tests from hanging forever 111 and eating up the CI resources. 112* **Controlling the virtual time**: in case just skipping delays is not sufficient, it's possible to more carefully 113 guide the execution, advancing the virtual time by a duration, draining the queue of the awaiting tasks, or running 114 the tasks scheduled at the present moment. 115* **Handling uncaught exceptions** spawned in the child coroutines by throwing them at the end of the test. 116* **Waiting for asynchronous callbacks**. 117 Sometimes, especially when working with third-party code, it's impossible to mock all the dispatchers in use. 118 [runTest] will handle the situations where some code runs in dispatchers not integrated with the test module. 119 120## Timeout 121 122Test automatically time out after 10 seconds. For example, this test will fail with a timeout exception: 123 124```kotlin 125@Test 126fun testHanging() = runTest { 127 CompletableDeferred<Unit>().await() // will hang forever 128} 129``` 130 131In case the test is expected to take longer than 10 seconds, the timeout can be increased by passing the `timeout` 132parameter: 133 134```kotlin 135@Test 136fun testTakingALongTime() = runTest(timeout = 30.seconds) { 137 val result = withContext(Dispatchers.Default) { 138 delay(20.seconds) // this delay is not in the test dispatcher and will not be skipped 139 3 140 } 141 assertEquals(3, result) 142} 143``` 144 145## Delay-skipping 146 147To test regular suspend functions, which may have a delay, just run them inside the [runTest] block. 148 149```kotlin 150@Test 151fun testFoo() = runTest { // a coroutine with an extra test control 152 val actual = foo() 153 // ... 154} 155 156suspend fun foo() { 157 delay(1_000) // when run in `runTest`, will finish immediately instead of delaying 158 // ... 159} 160``` 161 162## `launch` and `async` 163 164The coroutine dispatcher used for tests is single-threaded, meaning that the child coroutines of the [runTest] block 165will run on the thread that started the test, and will never run in parallel. 166 167If several coroutines are waiting to be executed next, the one scheduled after the smallest delay will be chosen. 168The virtual time will automatically advance to the point of its resumption. 169 170```kotlin 171@Test 172fun testWithMultipleDelays() = runTest { 173 launch { 174 delay(1_000) 175 println("1. $currentTime") // 1000 176 delay(200) 177 println("2. $currentTime") // 1200 178 delay(2_000) 179 println("4. $currentTime") // 3200 180 } 181 val deferred = async { 182 delay(3_000) 183 println("3. $currentTime") // 3000 184 delay(500) 185 println("5. $currentTime") // 3500 186 } 187 deferred.await() 188} 189``` 190 191## Controlling the virtual time 192 193Inside [runTest], the execution is scheduled by [TestCoroutineScheduler], which is a virtual time scheduler. 194The scheduler has several special methods that allow controlling the virtual time: 195* `currentTime` gets the current virtual time. 196* `runCurrent()` runs the tasks that are scheduled at this point of virtual time. 197* `advanceUntilIdle()` runs all enqueued tasks until there are no more. 198* `advanceTimeBy(timeDelta)` runs the enqueued tasks until the current virtual time advances by `timeDelta`. 199* `timeSource` returns a `TimeSource` that uses the virtual time. 200 201```kotlin 202@Test 203fun testFoo() = runTest { 204 launch { 205 val workDuration = testScheduler.timeSource.measureTime { 206 println(1) // executes during runCurrent() 207 delay(1_000) // suspends until time is advanced by at least 1_000 208 println(2) // executes during advanceTimeBy(2_000) 209 delay(500) // suspends until the time is advanced by another 500 ms 210 println(3) // also executes during advanceTimeBy(2_000) 211 delay(5_000) // will suspend by another 4_500 ms 212 println(4) // executes during advanceUntilIdle() 213 } 214 assertEquals(6500.milliseconds, workDuration) // the work took 6_500 ms of virtual time 215 } 216 // the child coroutine has not run yet 217 testScheduler.runCurrent() 218 // the child coroutine has called println(1), and is suspended on delay(1_000) 219 testScheduler.advanceTimeBy(2.seconds) // progress time, this will cause two calls to `delay` to resume 220 // the child coroutine has called println(2) and println(3) and suspends for another 4_500 virtual milliseconds 221 testScheduler.advanceUntilIdle() // will run the child coroutine to completion 222 assertEquals(6500, currentTime) // the child coroutine finished at virtual time of 6_500 milliseconds 223} 224``` 225 226## Using multiple test dispatchers 227 228The virtual time is controlled by an entity called the [TestCoroutineScheduler], which behaves as the shared source of 229virtual time. 230 231Several dispatchers can be created that use the same [TestCoroutineScheduler], in which case they will share their 232knowledge of the virtual time. 233 234To access the scheduler used for this test, use the [TestScope.testScheduler] property. 235 236```kotlin 237@Test 238fun testWithMultipleDispatchers() = runTest { 239 val scheduler = testScheduler // the scheduler used for this test 240 val dispatcher1 = StandardTestDispatcher(scheduler, name = "IO dispatcher") 241 val dispatcher2 = StandardTestDispatcher(scheduler, name = "Background dispatcher") 242 launch(dispatcher1) { 243 delay(1_000) 244 println("1. $currentTime") // 1000 245 delay(200) 246 println("2. $currentTime") // 1200 247 delay(2_000) 248 println("4. $currentTime") // 3200 249 } 250 val deferred = async(dispatcher2) { 251 delay(3_000) 252 println("3. $currentTime") // 3000 253 delay(500) 254 println("5. $currentTime") // 3500 255 } 256 deferred.await() 257 } 258``` 259 260**Note: if [Dispatchers.Main] is replaced by a [TestDispatcher], [runTest] will automatically use its scheduler. 261This is done so that there is no need to go through the ceremony of passing the correct scheduler to [runTest].** 262 263## Accessing the test coroutine scope 264 265Structured concurrency ties coroutines to scopes in which they are launched. 266[TestScope] is a special coroutine scope designed for testing coroutines, and a new one is automatically created 267for [runTest] and used as the receiver for the test body. 268 269However, it can be convenient to access a `CoroutineScope` before the test has started, for example, to perform mocking 270of some 271parts of the system in `@BeforeTest` via dependency injection. 272In these cases, it is possible to manually create [TestScope], the scope for the test coroutines, in advance, 273before the test begins. 274 275[TestScope] on its own does not automatically run the code launched in it. 276In addition, it is stateful in order to keep track of executing coroutines and uncaught exceptions. 277Therefore, it is important to ensure that [TestScope.runTest] is called eventually. 278 279```kotlin 280val scope = TestScope() 281 282@BeforeTest 283fun setUp() { 284 Dispatchers.setMain(StandardTestDispatcher(scope.testScheduler)) 285 TestSubject.setScope(scope) 286} 287 288@AfterTest 289fun tearDown() { 290 Dispatchers.resetMain() 291 TestSubject.resetScope() 292} 293 294@Test 295fun testSubject() = scope.runTest { 296 // the receiver here is `testScope` 297} 298``` 299 300## Running background work 301 302Sometimes, the fact that [runTest] waits for all the coroutines to finish is undesired. 303For example, the system under test may need to receive data from coroutines that always run in the background. 304Emulating such coroutines by launching them from the test body is not sufficient, because [runTest] will wait for them 305to finish, which they never typically do. 306 307For these cases, there is a special coroutine scope: [TestScope.backgroundScope]. 308Coroutines launched in it will be cancelled at the end of the test. 309 310```kotlin 311@Test 312fun testExampleBackgroundJob() = runTest { 313 val channel = Channel<Int>() 314 backgroundScope.launch { 315 var i = 0 316 while (true) { 317 channel.send(i++) 318 } 319 } 320 repeat(100) { 321 assertEquals(it, channel.receive()) 322 } 323} 324``` 325 326## Eagerly entering `launch` and `async` blocks 327 328Some tests only test functionality and don't particularly care about the precise order in which coroutines are 329dispatched. 330In these cases, it can be cumbersome to always call [runCurrent] or [yield] to observe the effects of the coroutines 331after they are launched. 332 333If [runTest] executes with an [UnconfinedTestDispatcher], the child coroutines launched at the top level are entered 334*eagerly*, that is, they don't go through a dispatch until the first suspension. 335 336```kotlin 337@Test 338fun testEagerlyEnteringChildCoroutines() = runTest(UnconfinedTestDispatcher()) { 339 var entered = false 340 val deferred = CompletableDeferred<Unit>() 341 var completed = false 342 launch { 343 entered = true 344 deferred.await() 345 completed = true 346 } 347 assertTrue(entered) // `entered = true` already executed. 348 assertFalse(completed) // however, the child coroutine then suspended, so it is enqueued. 349 deferred.complete(Unit) // resume the coroutine. 350 assertTrue(completed) // now the child coroutine is immediately completed. 351} 352``` 353 354If this behavior is desirable, but some parts of the test still require accurate dispatching, for example, to ensure 355that the code executes on the correct thread, then simply `launch` a new coroutine with the [StandardTestDispatcher]. 356 357```kotlin 358@Test 359fun testEagerlyEnteringSomeChildCoroutines() = runTest(UnconfinedTestDispatcher()) { 360 var entered1 = false 361 launch { 362 entered1 = true 363 } 364 assertTrue(entered1) // `entered1 = true` already executed 365 366 var entered2 = false 367 launch(StandardTestDispatcher(testScheduler)) { 368 // this block and every coroutine launched inside it will explicitly go through the needed dispatches 369 entered2 = true 370 } 371 assertFalse(entered2) 372 runCurrent() // need to explicitly run the dispatched continuation 373 assertTrue(entered2) 374} 375``` 376 377### Using `withTimeout` inside `runTest` 378 379Timeouts are also susceptible to time control, so the code below will immediately finish. 380 381```kotlin 382@Test 383fun testFooWithTimeout() = runTest { 384 assertFailsWith<TimeoutCancellationException> { 385 withTimeout(1_000) { 386 delay(999) 387 delay(2) 388 println("this won't be reached") 389 } 390 } 391} 392``` 393 394## Virtual time support with other dispatchers 395 396Calls to `withContext(Dispatchers.IO)`, `withContext(Dispatchers.Default)` ,and `withContext(Dispatchers.Main)` are 397common in coroutines-based code bases. Unfortunately, just executing code in a test will not lead to these dispatchers 398using the virtual time source, so delays will not be skipped in them. 399 400```kotlin 401suspend fun veryExpensiveFunction() = withContext(Dispatchers.Default) { 402 delay(1_000) 403 1 404} 405 406fun testExpensiveFunction() = runTest { 407 val result = veryExpensiveFunction() // will take a whole real-time second to execute 408 // the virtual time at this point is still 0 409} 410``` 411 412Tests should, when possible, replace these dispatchers with a [TestDispatcher] if the `withContext` calls `delay` in the 413function under test. For example, `veryExpensiveFunction` above should allow mocking with a [TestDispatcher] using 414either dependency injection, a service locator, or a default parameter, if it is to be used with virtual time. 415 416### Status of the API 417 418Many parts of the API is experimental, and it is may change before migrating out of experimental (while it is marked as 419[`@ExperimentalCoroutinesApi`][ExperimentalCoroutinesApi]). 420Changes during experimental may have deprecation applied when possible, but it is not 421advised to use the API in stable code before it leaves experimental due to possible breaking changes. 422 423If you have any suggestions for improvements to this experimental API please share them on the 424[issue tracker](https://github.com/Kotlin/kotlinx.coroutines/issues). 425 426<!--- MODULE kotlinx-coroutines-core --> 427<!--- INDEX kotlinx.coroutines --> 428 429[CoroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html 430[CoroutineDispatcher]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html 431[Dispatchers.Unconfined]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-unconfined.html 432[Dispatchers.Main]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html 433[yield]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/yield.html 434[ExperimentalCoroutinesApi]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-experimental-coroutines-api/index.html 435 436<!--- MODULE kotlinx-coroutines-test --> 437<!--- INDEX kotlinx.coroutines.test --> 438 439[runTest]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-test.html 440[TestCoroutineScheduler]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/index.html 441[TestScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-scope/index.html 442[TestDispatcher]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-dispatcher/index.html 443[Dispatchers.setMain]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/set-main.html 444[StandardTestDispatcher]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-standard-test-dispatcher.html 445[UnconfinedTestDispatcher]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-unconfined-test-dispatcher.html 446[setMain]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/set-main.html 447[TestScope.testScheduler]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-scope/test-scheduler.html 448[TestScope.runTest]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-test.html 449[TestScope.backgroundScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-scope/background-scope.html 450[runCurrent]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-current.html 451 452<!--- END --> 453