Name Date Size #Lines LOC

..--

api/H25-Apr-2025-136121

common/H25-Apr-2025-3,4232,233

js/H25-Apr-2025-5443

jvm/H25-Apr-2025-2,9512,261

native/H25-Apr-2025-3728

npm/H25-Apr-2025-2926

wasmJs/H25-Apr-2025-5545

MIGRATION.mdH A D25-Apr-202518.3 KiB448355

README.mdH A D25-Apr-202518 KiB453367

build.gradle.ktsH A D25-Apr-2025754 3126

README.md

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