1# Module kotlinx-coroutines-test 2 3Test utilities for `kotlinx.coroutines`. 4 5This package provides testing utilities for effectively testing coroutines. 6 7## Using in your project 8 9Add `kotlinx-coroutines-test` to your project test dependencies: 10``` 11dependencies { 12 testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.1' 13} 14``` 15 16**Do not** depend on this project in your main sources, all utilities are intended and designed to be used only from tests. 17 18## Dispatchers.Main Delegation 19 20`Dispatchers.setMain` will override the `Main` dispatcher in test situations. This is helpful when you want to execute a 21test in situations where the platform `Main` dispatcher is not available, or you wish to replace `Dispatchers.Main` with a 22testing dispatcher. 23 24Once you have this dependency in the runtime, 25[`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) mechanism will overwrite 26[Dispatchers.Main] with a testable implementation. 27 28You can override the `Main` implementation using [setMain][setMain] method with any [CoroutineDispatcher] implementation, e.g.: 29 30```kotlin 31 32class SomeTest { 33 34 private val mainThreadSurrogate = newSingleThreadContext("UI thread") 35 36 @Before 37 fun setUp() { 38 Dispatchers.setMain(mainThreadSurrogate) 39 } 40 41 @After 42 fun tearDown() { 43 Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher 44 mainThreadSurrogate.close() 45 } 46 47 @Test 48 fun testSomeUI() = runBlocking { 49 launch(Dispatchers.Main) { // Will be launched in the mainThreadSurrogate dispatcher 50 // ... 51 } 52 } 53} 54``` 55Calling `setMain` or `resetMain` immediately changes the `Main` dispatcher globally. The testable version of 56`Dispatchers.Main` installed by the `ServiceLoader` will delegate to the dispatcher provided by `setMain`. 57 58## runBlockingTest 59 60To test regular suspend functions or coroutines started with `launch` or `async` use the [runBlockingTest] coroutine 61builder that provides extra test control to coroutines. 62 631. Auto-advancing of time for regular suspend functions 642. Explicit time control for testing multiple coroutines 653. Eager execution of `launch` or `async` code blocks 664. Pause, manually advance, and restart the execution of coroutines in a test 675. Report uncaught exceptions as test failures 68 69### Testing regular suspend functions 70 71To test regular suspend functions, which may have a delay, you can use the [runBlockingTest] builder to start a testing 72coroutine. Any calls to `delay` will automatically advance virtual time by the amount delayed. 73 74```kotlin 75@Test 76fun testFoo() = runBlockingTest { // a coroutine with an extra test control 77 val actual = foo() 78 // ... 79} 80 81suspend fun foo() { 82 delay(1_000) // auto-advances virtual time by 1_000ms due to runBlockingTest 83 // ... 84} 85``` 86 87`runBlockingTest` returns `Unit` so it may be used in a single expression with common testing libraries. 88 89### Testing `launch` or `async` 90 91Inside of [runBlockingTest], both [launch] and [async] will start a new coroutine that may run concurrently with the 92test case. 93 94To make common testing situations easier, by default the body of the coroutine is executed *eagerly* until 95the first call to [delay] or [yield]. 96 97```kotlin 98@Test 99fun testFooWithLaunch() = runBlockingTest { 100 foo() 101 // the coroutine launched by foo() is completed before foo() returns 102 // ... 103} 104 105fun CoroutineScope.foo() { 106 // This coroutines `Job` is not shared with the test code 107 launch { 108 bar() // executes eagerly when foo() is called due to runBlockingTest 109 println(1) // executes eagerly when foo() is called due to runBlockingTest 110 } 111} 112 113suspend fun bar() {} 114``` 115 116`runBlockingTest` will auto-progress virtual time until all coroutines are completed before returning. If any coroutines 117are not able to complete, an [UncompletedCoroutinesError] will be thrown. 118 119*Note:* The default eager behavior of [runBlockingTest] will ignore [CoroutineStart] parameters. 120 121### Testing `launch` or `async` with `delay` 122 123If the coroutine created by `launch` or `async` calls `delay` then the [runBlockingTest] will not auto-progress time 124right away. This allows tests to observe the interaction of multiple coroutines with different delays. 125 126To control time in the test you can use the [DelayController] interface. The block passed to 127[runBlockingTest] can call any method on the `DelayController` interface. 128 129```kotlin 130@Test 131fun testFooWithLaunchAndDelay() = runBlockingTest { 132 foo() 133 // the coroutine launched by foo has not completed here, it is suspended waiting for delay(1_000) 134 advanceTimeBy(1_000) // progress time, this will cause the delay to resume 135 // the coroutine launched by foo has completed here 136 // ... 137} 138 139suspend fun CoroutineScope.foo() { 140 launch { 141 println(1) // executes eagerly when foo() is called due to runBlockingTest 142 delay(1_000) // suspends until time is advanced by at least 1_000 143 println(2) // executes after advanceTimeBy(1_000) 144 } 145} 146``` 147 148*Note:* `runBlockingTest` will always attempt to auto-progress time until all coroutines are completed just before 149exiting. This is a convenience to avoid having to call [advanceUntilIdle][DelayController.advanceUntilIdle] 150as the last line of many common test cases. 151If any coroutines cannot complete by advancing time, an [UncompletedCoroutinesError] is thrown. 152 153### Testing `withTimeout` using `runBlockingTest` 154 155Time control can be used to test timeout code. To do so, ensure that the function under test is suspended inside a 156`withTimeout` block and advance time until the timeout is triggered. 157 158Depending on the code, causing the code to suspend may need to use different mocking or fake techniques. For this 159example an uncompleted `Deferred<Foo>` is provided to the function under test via parameter injection. 160 161```kotlin 162@Test(expected = TimeoutCancellationException::class) 163fun testFooWithTimeout() = runBlockingTest { 164 val uncompleted = CompletableDeferred<Foo>() // this Deferred<Foo> will never complete 165 foo(uncompleted) 166 advanceTimeBy(1_000) // advance time, which will cause the timeout to throw an exception 167 // ... 168} 169 170fun CoroutineScope.foo(resultDeferred: Deferred<Foo>) { 171 launch { 172 withTimeout(1_000) { 173 resultDeferred.await() // await() will suspend forever waiting for uncompleted 174 // ... 175 } 176 } 177} 178``` 179 180*Note:* Testing timeouts is simpler with a second coroutine that can be suspended (as in this example). If the 181call to `withTimeout` is in a regular suspend function, consider calling `launch` or `async` inside your test body to 182create a second coroutine. 183 184### Using `pauseDispatcher` for explicit execution of `runBlockingTest` 185 186The eager execution of `launch` and `async` bodies makes many tests easier, but some tests need more fine grained 187control of coroutine execution. 188 189To disable eager execution, you can call [pauseDispatcher][DelayController.pauseDispatcher] 190to pause the [TestCoroutineDispatcher] that [runBlockingTest] uses. 191 192When the dispatcher is paused, all coroutines will be added to a queue instead running. In addition, time will never 193auto-progress due to `delay` on a paused dispatcher. 194 195```kotlin 196@Test 197fun testFooWithPauseDispatcher() = runBlockingTest { 198 pauseDispatcher { 199 foo() 200 // the coroutine started by foo has not run yet 201 runCurrent() // the coroutine started by foo advances to delay(1_000) 202 // the coroutine started by foo has called println(1), and is suspended on delay(1_000) 203 advanceTimeBy(1_000) // progress time, this will cause the delay to resume 204 // the coroutine started by foo has called println(2) and has completed here 205 } 206 // ... 207} 208 209fun CoroutineScope.foo() { 210 launch { 211 println(1) // executes after runCurrent() is called 212 delay(1_000) // suspends until time is advanced by at least 1_000 213 println(2) // executes after advanceTimeBy(1_000) 214 } 215} 216``` 217 218Using `pauseDispatcher` gives tests explicit control over the progress of time as well as the ability to enqueue all 219coroutines. As a best practice consider adding two tests, one paused and one eager, to test coroutines that have 220non-trivial external dependencies and side effects in their launch body. 221 222*Important:* When passed a lambda block, `pauseDispatcher` will resume eager execution immediately after the block. 223This will cause time to auto-progress if there are any outstanding `delay` calls that were not resolved before the 224`pauseDispatcher` block returned. In advanced situations tests can call [pauseDispatcher][DelayController.pauseDispatcher] 225without a lambda block and then explicitly resume the dispatcher with [resumeDispatcher][DelayController.resumeDispatcher]. 226 227## Integrating tests with structured concurrency 228 229Code that uses structured concurrency needs a [CoroutineScope] in order to launch a coroutine. In order to integrate 230[runBlockingTest] with code that uses common structured concurrency patterns tests can provide one (or both) of these 231classes to application code. 232 233 | Name | Description | 234 | ---- | ----------- | 235 | [TestCoroutineScope] | A [CoroutineScope] which provides detailed control over the execution of coroutines for tests and integrates with [runBlockingTest]. | 236 | [TestCoroutineDispatcher] | A [CoroutineDispatcher] which can be used for tests and integrates with [runBlockingTest]. | 237 238 Both classes are provided to allow for various testing needs. Depending on the code that's being 239 tested, it may be easier to provide a [TestCoroutineDispatcher]. For example [Dispatchers.setMain][setMain] will accept 240 a [TestCoroutineDispatcher] but not a [TestCoroutineScope]. 241 242 [TestCoroutineScope] will always use a [TestCoroutineDispatcher] to execute coroutines. It 243 also uses [TestCoroutineExceptionHandler] to convert uncaught exceptions into test failures. 244 245By providing [TestCoroutineScope] a test case is able to control execution of coroutines, as well as ensure that 246uncaught exceptions thrown by coroutines are converted into test failures. 247 248### Providing `TestCoroutineScope` from `runBlockingTest` 249 250In simple cases, tests can use the [TestCoroutineScope] created by [runBlockingTest] directly. 251 252```kotlin 253@Test 254fun testFoo() = runBlockingTest { 255 foo() // runBlockingTest passed in a TestCoroutineScope as this 256} 257 258fun CoroutineScope.foo() { 259 launch { // CoroutineScope for launch is the TestCoroutineScope provided by runBlockingTest 260 // ... 261 } 262} 263``` 264 265This style is preferred when the `CoroutineScope` is passed through an extension function style. 266 267### Providing an explicit `TestCoroutineScope` 268 269In many cases, the direct style is not preferred because [CoroutineScope] may need to be provided through another means 270such as dependency injection or service locators. 271 272Tests can declare a [TestCoroutineScope] explicitly in the class to support these use cases. 273 274Since [TestCoroutineScope] is stateful in order to keep track of executing coroutines and uncaught exceptions, it is 275important to ensure that [cleanupTestCoroutines][TestCoroutineScope.cleanupTestCoroutines] is called after every test case. 276 277```kotlin 278class TestClass { 279 private val testScope = TestCoroutineScope() 280 private lateinit var subject: Subject 281 282 @Before 283 fun setup() { 284 // provide the scope explicitly, in this example using a constructor parameter 285 subject = Subject(testScope) 286 } 287 288 @After 289 fun cleanUp() { 290 testScope.cleanupTestCoroutines() 291 } 292 293 @Test 294 fun testFoo() = testScope.runBlockingTest { 295 // TestCoroutineScope.runBlockingTest uses the Dispatcher and exception handler provided by `testScope` 296 subject.foo() 297 } 298} 299 300class Subject(val scope: CoroutineScope) { 301 fun foo() { 302 scope.launch { 303 // launch uses the testScope injected in setup 304 } 305 } 306} 307``` 308 309*Note:* [TestCoroutineScope], [TestCoroutineDispatcher], and [TestCoroutineExceptionHandler] are interfaces to enable 310test libraries to provide library specific integrations. For example, a JUnit4 `@Rule` may call 311[Dispatchers.setMain][setMain] then expose [TestCoroutineScope] for use in tests. 312 313### Providing an explicit `TestCoroutineDispatcher` 314 315While providing a [TestCoroutineScope] is slightly preferred due to the improved uncaught exception handling, there are 316many situations where it is easier to provide a [TestCoroutineDispatcher]. For example [Dispatchers.setMain][setMain] 317does not accept a [TestCoroutineScope] and requires a [TestCoroutineDispatcher] to control coroutine execution in 318tests. 319 320The main difference between `TestCoroutineScope` and `TestCoroutineDispatcher` is how uncaught exceptions are handled. 321When using `TestCoroutineDispatcher` uncaught exceptions thrown in coroutines will use regular 322[coroutine exception handling](https://kotlinlang.org/docs/reference/coroutines/exception-handling.html). 323`TestCoroutineScope` will always use `TestCoroutineDispatcher` as it's dispatcher. 324 325A test can use a `TestCoroutineDispatcher` without declaring an explicit `TestCoroutineScope`. This is preferred 326when the class under test allows a test to provide a [CoroutineDispatcher] but does not allow the test to provide a 327[CoroutineScope]. 328 329Since [TestCoroutineDispatcher] is stateful in order to keep track of executing coroutines, it is 330important to ensure that [cleanupTestCoroutines][DelayController.cleanupTestCoroutines] is called after every test case. 331 332```kotlin 333class TestClass { 334 private val testDispatcher = TestCoroutineDispatcher() 335 336 @Before 337 fun setup() { 338 // provide the scope explicitly, in this example using a constructor parameter 339 Dispatchers.setMain(testDispatcher) 340 } 341 342 @After 343 fun cleanUp() { 344 Dispatchers.resetMain() 345 testDispatcher.cleanupTestCoroutines() 346 } 347 348 @Test 349 fun testFoo() = testDispatcher.runBlockingTest { 350 // TestCoroutineDispatcher.runBlockingTest uses `testDispatcher` to run coroutines 351 foo() 352 } 353} 354 355fun foo() { 356 MainScope().launch { 357 // launch will use the testDispatcher provided by setMain 358 } 359} 360``` 361 362*Note:* Prefer to provide `TestCoroutineScope` when it does not complicate code since it will also elevate exceptions 363to test failures. However, exposing a `CoroutineScope` to callers of a function may lead to complicated code, in which 364case this is the preferred pattern. 365 366### Using `TestCoroutineScope` and `TestCoroutineDispatcher` without `runBlockingTest` 367 368It is supported to use both [TestCoroutineScope] and [TestCoroutineDispatcher] without using the [runBlockingTest] 369builder. Tests may need to do this in situations such as introducing multiple dispatchers and library writers may do 370this to provide alternatives to `runBlockingTest`. 371 372```kotlin 373@Test 374fun testFooWithAutoProgress() { 375 val scope = TestCoroutineScope() 376 scope.foo() 377 // foo is suspended waiting for time to progress 378 scope.advanceUntilIdle() 379 // foo's coroutine will be completed before here 380} 381 382fun CoroutineScope.foo() { 383 launch { 384 println(1) // executes eagerly when foo() is called due to TestCoroutineScope 385 delay(1_000) // suspends until time is advanced by at least 1_000 386 println(2) // executes after advanceTimeUntilIdle 387 } 388} 389``` 390 391## Using time control with `withContext` 392 393Calls to `withContext(Dispatchers.IO)` or `withContext(Dispatchers.Default)` are common in coroutines based codebases. 394Both dispatchers are not designed to interact with `TestCoroutineDispatcher`. 395 396Tests should provide a `TestCoroutineDispatcher` to replace these dispatchers if the `withContext` calls `delay` in the 397function under test. For example, a test that calls `veryExpensiveOne` should provide a `TestCoroutineDispatcher` using 398either dependency injection, a service locator, or a default parameter. 399 400```kotlin 401suspend fun veryExpensiveOne() = withContext(Dispatchers.Default) { 402 delay(1_000) 403 1 // for very expensive values of 1 404} 405``` 406 407In situations where the code inside the `withContext` is very simple, it is not as important to provide a test 408dispatcher. The function `veryExpensiveTwo` will behave identically in a `TestCoroutineDispatcher` and 409`Dispatchers.Default` after the thread switch for `Dispatchers.Default`. Because `withContext` always returns a value by 410directly, there is no need to inject a `TestCoroutineDispatcher` into this function. 411 412```kotlin 413suspend fun veryExpensiveTwo() = withContext(Dispatchers.Default) { 414 2 // for very expensive values of 2 415} 416``` 417 418Tests should provide a `TestCoroutineDispatcher` to code that calls `withContext` to provide time control for 419delays, or when execution control is needed to test complex logic. 420 421 422### Status of the API 423 424This API is experimental and it is may change before migrating out of experimental (while it is marked as 425[`@ExperimentalCoroutinesApi`][ExperimentalCoroutinesApi]). 426Changes during experimental may have deprecation applied when possible, but it is not 427advised to use the API in stable code before it leaves experimental due to possible breaking changes. 428 429If you have any suggestions for improvements to this experimental API please share them them on the 430[issue tracker](https://github.com/Kotlin/kotlinx.coroutines/issues). 431 432<!--- MODULE kotlinx-coroutines-core --> 433<!--- INDEX kotlinx.coroutines --> 434[Dispatchers.Main]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html 435[CoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html 436[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html 437[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html 438[delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html 439[yield]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/yield.html 440[CoroutineStart]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/index.html 441[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html 442[ExperimentalCoroutinesApi]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-experimental-coroutines-api/index.html 443<!--- MODULE kotlinx-coroutines-test --> 444<!--- INDEX kotlinx.coroutines.test --> 445[setMain]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/kotlinx.coroutines.-dispatchers/set-main.html 446[runBlockingTest]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-blocking-test.html 447[UncompletedCoroutinesError]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-uncompleted-coroutines-error/index.html 448[DelayController]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/index.html 449[DelayController.advanceUntilIdle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/advance-until-idle.html 450[DelayController.pauseDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/pause-dispatcher.html 451[TestCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-dispatcher/index.html 452[DelayController.resumeDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/resume-dispatcher.html 453[TestCoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scope/index.html 454[TestCoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-exception-handler/index.html 455[TestCoroutineScope.cleanupTestCoroutines]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scope/cleanup-test-coroutines.html 456[DelayController.cleanupTestCoroutines]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/cleanup-test-coroutines.html 457<!--- END --> 458