• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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