blob: 0633e6f235c8e87cde21080d62c9ec4681fc87a6 [file] [log] [blame] [view]
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +03001# Module kotlinx-coroutines-test
2
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -07003Test utilities for `kotlinx.coroutines`.
4
dkhalanskyjb2e25bae2021-11-19 21:02:52 +03005## 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]. |
NWuensche3b423b92022-03-09 08:57:35 +000014| [TestDispatcher] | A [CoroutineDispatcher] whose delays are controlled by a [TestCoroutineScheduler]. |
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030015| [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]. |
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +030023
24## Using in your project
25
26Add `kotlinx-coroutines-test` to your project test dependencies:
27```
28dependencies {
Dmitry Khalanskiy60d2fe82024-03-11 08:55:01 +010029 testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1-Beta'
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +030030}
31```
32
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030033**Do not** depend on this project in your main sources, all utilities here are intended and designed to be used only from tests.
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +030034
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -070035## Dispatchers.Main Delegation
Roman Elizarov445e7982019-04-21 11:21:59 +030036
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030037`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.
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -070040
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030041On 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.
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +030045
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030046The `Main` implementation can be overridden using [Dispatchers.setMain][setMain] method with any [CoroutineDispatcher]
47implementation, e.g.:
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +030048
49```kotlin
50
51class SomeTest {
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030052
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +030053 private val mainThreadSurrogate = newSingleThreadContext("UI thread")
54
55 @Before
56 fun setUp() {
57 Dispatchers.setMain(mainThreadSurrogate)
58 }
59
60 @After
61 fun tearDown() {
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030062 Dispatchers.resetMain() // reset the main dispatcher to the original Main dispatcher
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +030063 mainThreadSurrogate.close()
64 }
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030065
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +030066 @Test
67 fun testSomeUI() = runBlocking {
68 launch(Dispatchers.Main) { // Will be launched in the mainThreadSurrogate dispatcher
Roman Elizarov445e7982019-04-21 11:21:59 +030069 // ...
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +030070 }
71 }
72}
73```
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -070074
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030075Calling `setMain` or `resetMain` immediately changes the `Main` dispatcher globally.
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -070076
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030077If `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.
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -070079
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030080## runTest
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -070081
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030082[runTest] is the way to test code that involves coroutines. `suspend` functions can be called inside it.
Roman Elizarov445e7982019-04-21 11:21:59 +030083
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030084**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:
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -070086
87```kotlin
88@Test
dkhalanskyjb2e25bae2021-11-19 21:02:52 +030089fun 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.
Dmitry Khalanskiycb0ef712023-02-27 18:04:23 +0300110* **The execution times out after 10 seconds**, cancelling the test coroutine to prevent tests from hanging forever
111 and eating up the CI resources.
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300112* **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
Dmitry Khalanskiycb0ef712023-02-27 18:04:23 +0300120## 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
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300145## 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()
Roman Elizarov445e7982019-04-21 11:21:59 +0300153 // ...
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700154}
155
156suspend fun foo() {
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300157 delay(1_000) // when run in `runTest`, will finish immediately instead of delaying
Roman Elizarov445e7982019-04-21 11:21:59 +0300158 // ...
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700159}
160```
161
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300162## `launch` and `async`
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700163
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300164The 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.
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700166
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300167If 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.
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700169
170```kotlin
171@Test
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300172fun testWithMultipleDelays() = runTest {
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700173 launch {
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300174 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
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700180 }
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300181 val deferred = async {
182 delay(3_000)
183 println("3. $currentTime") // 3000
184 delay(500)
185 println("5. $currentTime") // 3500
186 }
187 deferred.await()
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700188}
189```
190
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300191## Controlling the virtual time
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700192
Dmitry Khalanskiycb0ef712023-02-27 18:04:23 +0300193Inside [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:
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300195* `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`.
Dmitry Khalanskiycb0ef712023-02-27 18:04:23 +0300199* `timeSource` returns a `TimeSource` that uses the virtual time.
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700200
201```kotlin
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300202@Test
203fun testFoo() = runTest {
204 launch {
Dmitry Khalanskiycb0ef712023-02-27 18:04:23 +0300205 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
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300215 }
216 // the child coroutine has not run yet
Dmitry Khalanskiycb0ef712023-02-27 18:04:23 +0300217 testScheduler.runCurrent()
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300218 // the child coroutine has called println(1), and is suspended on delay(1_000)
Dmitry Khalanskiycb0ef712023-02-27 18:04:23 +0300219 testScheduler.advanceTimeBy(2.seconds) // progress time, this will cause two calls to `delay` to resume
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300220 // the child coroutine has called println(2) and println(3) and suspends for another 4_500 virtual milliseconds
Dmitry Khalanskiycb0ef712023-02-27 18:04:23 +0300221 testScheduler.advanceUntilIdle() // will run the child coroutine to completion
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300222 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)
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700286}
287
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300288@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
Dmitry Khalanskiycb0ef712023-02-27 18:04:23 +0300300## 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
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300326## 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
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700342 launch {
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300343 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> {
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700385 withTimeout(1_000) {
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300386 delay(999)
387 delay(2)
388 println("this won't be reached")
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700389 }
390 }
391}
392```
393
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300394## Virtual time support with other dispatchers
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700395
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300396Calls 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.
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700399
400```kotlin
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300401suspend fun veryExpensiveFunction() = withContext(Dispatchers.Default) {
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700402 delay(1_000)
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300403 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
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700409}
410```
411
dkhalanskyjb2e25bae2021-11-19 21:02:52 +0300412Tests 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.
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700415
416### Status of the API
417
Dmitry Khalanskiycb0ef712023-02-27 18:04:23 +0300418Many parts of the API is experimental, and it is may change before migrating out of experimental (while it is marked as
Roman Elizarov445e7982019-04-21 11:21:59 +0300419[`@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.
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700422
Mohammad Ershad Nasri774e66d2022-04-11 01:10:54 -0700423If you have any suggestions for improvements to this experimental API please share them on the
Sean McQuillanc0a8d1c2019-04-20 18:10:09 -0700424[issue tracker](https://github.com/Kotlin/kotlinx.coroutines/issues).
425
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +0300426<!--- MODULE kotlinx-coroutines-core -->
427<!--- INDEX kotlinx.coroutines -->
Vsevolod Tolstopyatov167c44e2020-11-30 05:36:23 -0800428
zoobestik74cf9ea2022-06-23 16:51:33 +0300429[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
Vsevolod Tolstopyatov167c44e2020-11-30 05:36:23 -0800435
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +0300436<!--- MODULE kotlinx-coroutines-test -->
437<!--- INDEX kotlinx.coroutines.test -->
Vsevolod Tolstopyatov167c44e2020-11-30 05:36:23 -0800438
zoobestik74cf9ea2022-06-23 16:51:33 +0300439[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
Dmitry Khalanskiycb0ef712023-02-27 18:04:23 +0300449[TestScope.backgroundScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-scope/background-scope.html
zoobestik74cf9ea2022-06-23 16:51:33 +0300450[runCurrent]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-current.html
Vsevolod Tolstopyatov167c44e2020-11-30 05:36:23 -0800451
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +0300452<!--- END -->