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.6.4' 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* **Controlling the virtual time**: in case just skipping delays is not sufficient, it's possible to more carefully 111 guide the execution, advancing the virtual time by a duration, draining the queue of the awaiting tasks, or running 112 the tasks scheduled at the present moment. 113* **Handling uncaught exceptions** spawned in the child coroutines by throwing them at the end of the test. 114* **Waiting for asynchronous callbacks**. 115 Sometimes, especially when working with third-party code, it's impossible to mock all the dispatchers in use. 116 [runTest] will handle the situations where some code runs in dispatchers not integrated with the test module. 117 118## Delay-skipping 119 120To test regular suspend functions, which may have a delay, just run them inside the [runTest] block. 121 122```kotlin 123@Test 124fun testFoo() = runTest { // a coroutine with an extra test control 125 val actual = foo() 126 // ... 127} 128 129suspend fun foo() { 130 delay(1_000) // when run in `runTest`, will finish immediately instead of delaying 131 // ... 132} 133``` 134 135## `launch` and `async` 136 137The coroutine dispatcher used for tests is single-threaded, meaning that the child coroutines of the [runTest] block 138will run on the thread that started the test, and will never run in parallel. 139 140If several coroutines are waiting to be executed next, the one scheduled after the smallest delay will be chosen. 141The virtual time will automatically advance to the point of its resumption. 142 143```kotlin 144@Test 145fun testWithMultipleDelays() = runTest { 146 launch { 147 delay(1_000) 148 println("1. $currentTime") // 1000 149 delay(200) 150 println("2. $currentTime") // 1200 151 delay(2_000) 152 println("4. $currentTime") // 3200 153 } 154 val deferred = async { 155 delay(3_000) 156 println("3. $currentTime") // 3000 157 delay(500) 158 println("5. $currentTime") // 3500 159 } 160 deferred.await() 161} 162``` 163 164## Controlling the virtual time 165 166Inside [runTest], the following operations are supported: 167* `currentTime` gets the current virtual time. 168* `runCurrent()` runs the tasks that are scheduled at this point of virtual time. 169* `advanceUntilIdle()` runs all enqueued tasks until there are no more. 170* `advanceTimeBy(timeDelta)` runs the enqueued tasks until the current virtual time advances by `timeDelta`. 171 172```kotlin 173@Test 174fun testFoo() = runTest { 175 launch { 176 println(1) // executes during runCurrent() 177 delay(1_000) // suspends until time is advanced by at least 1_000 178 println(2) // executes during advanceTimeBy(2_000) 179 delay(500) // suspends until the time is advanced by another 500 ms 180 println(3) // also executes during advanceTimeBy(2_000) 181 delay(5_000) // will suspend by another 4_500 ms 182 println(4) // executes during advanceUntilIdle() 183 } 184 // the child coroutine has not run yet 185 runCurrent() 186 // the child coroutine has called println(1), and is suspended on delay(1_000) 187 advanceTimeBy(2_000) // progress time, this will cause two calls to `delay` to resume 188 // the child coroutine has called println(2) and println(3) and suspends for another 4_500 virtual milliseconds 189 advanceUntilIdle() // will run the child coroutine to completion 190 assertEquals(6500, currentTime) // the child coroutine finished at virtual time of 6_500 milliseconds 191} 192``` 193 194## Using multiple test dispatchers 195 196The virtual time is controlled by an entity called the [TestCoroutineScheduler], which behaves as the shared source of 197virtual time. 198 199Several dispatchers can be created that use the same [TestCoroutineScheduler], in which case they will share their 200knowledge of the virtual time. 201 202To access the scheduler used for this test, use the [TestScope.testScheduler] property. 203 204```kotlin 205@Test 206fun testWithMultipleDispatchers() = runTest { 207 val scheduler = testScheduler // the scheduler used for this test 208 val dispatcher1 = StandardTestDispatcher(scheduler, name = "IO dispatcher") 209 val dispatcher2 = StandardTestDispatcher(scheduler, name = "Background dispatcher") 210 launch(dispatcher1) { 211 delay(1_000) 212 println("1. $currentTime") // 1000 213 delay(200) 214 println("2. $currentTime") // 1200 215 delay(2_000) 216 println("4. $currentTime") // 3200 217 } 218 val deferred = async(dispatcher2) { 219 delay(3_000) 220 println("3. $currentTime") // 3000 221 delay(500) 222 println("5. $currentTime") // 3500 223 } 224 deferred.await() 225 } 226``` 227 228**Note: if [Dispatchers.Main] is replaced by a [TestDispatcher], [runTest] will automatically use its scheduler. 229This is done so that there is no need to go through the ceremony of passing the correct scheduler to [runTest].** 230 231## Accessing the test coroutine scope 232 233Structured concurrency ties coroutines to scopes in which they are launched. 234[TestScope] is a special coroutine scope designed for testing coroutines, and a new one is automatically created 235for [runTest] and used as the receiver for the test body. 236 237However, it can be convenient to access a `CoroutineScope` before the test has started, for example, to perform mocking 238of some 239parts of the system in `@BeforeTest` via dependency injection. 240In these cases, it is possible to manually create [TestScope], the scope for the test coroutines, in advance, 241before the test begins. 242 243[TestScope] on its own does not automatically run the code launched in it. 244In addition, it is stateful in order to keep track of executing coroutines and uncaught exceptions. 245Therefore, it is important to ensure that [TestScope.runTest] is called eventually. 246 247```kotlin 248val scope = TestScope() 249 250@BeforeTest 251fun setUp() { 252 Dispatchers.setMain(StandardTestDispatcher(scope.testScheduler)) 253 TestSubject.setScope(scope) 254} 255 256@AfterTest 257fun tearDown() { 258 Dispatchers.resetMain() 259 TestSubject.resetScope() 260} 261 262@Test 263fun testSubject() = scope.runTest { 264 // the receiver here is `testScope` 265} 266``` 267 268## Eagerly entering `launch` and `async` blocks 269 270Some tests only test functionality and don't particularly care about the precise order in which coroutines are 271dispatched. 272In these cases, it can be cumbersome to always call [runCurrent] or [yield] to observe the effects of the coroutines 273after they are launched. 274 275If [runTest] executes with an [UnconfinedTestDispatcher], the child coroutines launched at the top level are entered 276*eagerly*, that is, they don't go through a dispatch until the first suspension. 277 278```kotlin 279@Test 280fun testEagerlyEnteringChildCoroutines() = runTest(UnconfinedTestDispatcher()) { 281 var entered = false 282 val deferred = CompletableDeferred<Unit>() 283 var completed = false 284 launch { 285 entered = true 286 deferred.await() 287 completed = true 288 } 289 assertTrue(entered) // `entered = true` already executed. 290 assertFalse(completed) // however, the child coroutine then suspended, so it is enqueued. 291 deferred.complete(Unit) // resume the coroutine. 292 assertTrue(completed) // now the child coroutine is immediately completed. 293} 294``` 295 296If this behavior is desirable, but some parts of the test still require accurate dispatching, for example, to ensure 297that the code executes on the correct thread, then simply `launch` a new coroutine with the [StandardTestDispatcher]. 298 299```kotlin 300@Test 301fun testEagerlyEnteringSomeChildCoroutines() = runTest(UnconfinedTestDispatcher()) { 302 var entered1 = false 303 launch { 304 entered1 = true 305 } 306 assertTrue(entered1) // `entered1 = true` already executed 307 308 var entered2 = false 309 launch(StandardTestDispatcher(testScheduler)) { 310 // this block and every coroutine launched inside it will explicitly go through the needed dispatches 311 entered2 = true 312 } 313 assertFalse(entered2) 314 runCurrent() // need to explicitly run the dispatched continuation 315 assertTrue(entered2) 316} 317``` 318 319### Using `withTimeout` inside `runTest` 320 321Timeouts are also susceptible to time control, so the code below will immediately finish. 322 323```kotlin 324@Test 325fun testFooWithTimeout() = runTest { 326 assertFailsWith<TimeoutCancellationException> { 327 withTimeout(1_000) { 328 delay(999) 329 delay(2) 330 println("this won't be reached") 331 } 332 } 333} 334``` 335 336## Virtual time support with other dispatchers 337 338Calls to `withContext(Dispatchers.IO)`, `withContext(Dispatchers.Default)` ,and `withContext(Dispatchers.Main)` are 339common in coroutines-based code bases. Unfortunately, just executing code in a test will not lead to these dispatchers 340using the virtual time source, so delays will not be skipped in them. 341 342```kotlin 343suspend fun veryExpensiveFunction() = withContext(Dispatchers.Default) { 344 delay(1_000) 345 1 346} 347 348fun testExpensiveFunction() = runTest { 349 val result = veryExpensiveFunction() // will take a whole real-time second to execute 350 // the virtual time at this point is still 0 351} 352``` 353 354Tests should, when possible, replace these dispatchers with a [TestDispatcher] if the `withContext` calls `delay` in the 355function under test. For example, `veryExpensiveFunction` above should allow mocking with a [TestDispatcher] using 356either dependency injection, a service locator, or a default parameter, if it is to be used with virtual time. 357 358### Status of the API 359 360This API is experimental and it is may change before migrating out of experimental (while it is marked as 361[`@ExperimentalCoroutinesApi`][ExperimentalCoroutinesApi]). 362Changes during experimental may have deprecation applied when possible, but it is not 363advised to use the API in stable code before it leaves experimental due to possible breaking changes. 364 365If you have any suggestions for improvements to this experimental API please share them on the 366[issue tracker](https://github.com/Kotlin/kotlinx.coroutines/issues). 367 368<!--- MODULE kotlinx-coroutines-core --> 369<!--- INDEX kotlinx.coroutines --> 370 371[CoroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html 372[CoroutineDispatcher]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html 373[Dispatchers.Unconfined]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-unconfined.html 374[Dispatchers.Main]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html 375[yield]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/yield.html 376[ExperimentalCoroutinesApi]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-experimental-coroutines-api/index.html 377 378<!--- MODULE kotlinx-coroutines-test --> 379<!--- INDEX kotlinx.coroutines.test --> 380 381[runTest]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-test.html 382[TestCoroutineScheduler]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/index.html 383[TestScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-scope/index.html 384[TestDispatcher]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-dispatcher/index.html 385[Dispatchers.setMain]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/set-main.html 386[StandardTestDispatcher]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-standard-test-dispatcher.html 387[UnconfinedTestDispatcher]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-unconfined-test-dispatcher.html 388[setMain]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/set-main.html 389[TestScope.testScheduler]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-scope/test-scheduler.html 390[TestScope.runTest]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-test.html 391[runCurrent]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-current.html 392 393<!--- END --> 394