@@ -16,6 +16,7 @@ dependencies {
1616** Do not** depend on this project in your main sources, all utilities are intended and designed to be used only from tests.
1717
1818## Dispatchers.Main Delegation
19+
1920` Dispatchers.setMain ` will override the ` Main ` dispatcher in test situations. This is helpful when you want to execute a
2021test in situations where the platform ` Main ` dispatcher is not available, or you wish to replace ` Dispatchers.Main ` with a
2122testing dispatcher.
@@ -46,7 +47,7 @@ class SomeTest {
4647 @Test
4748 fun testSomeUI () = runBlocking {
4849 launch(Dispatchers .Main ) { // Will be launched in the mainThreadSurrogate dispatcher
49- .. .
50+ // ...
5051 }
5152 }
5253}
@@ -66,19 +67,20 @@ builder that provides extra test control to coroutines.
66675 . Report uncaught exceptions as test failures
6768
6869### Testing regular suspend functions
70+
6971To test regular suspend functions, which may have a delay, you can use the [ runBlockingTest] builder to start a testing
7072coroutine. Any calls to ` delay ` will automatically advance time.
7173
7274``` kotlin
7375@Test
7476fun testFoo () = runBlockingTest { // a coroutine with a extra test control
7577 val actual = foo()
76- .. .
78+ // ...
7779}
7880
7981suspend fun foo () {
8082 delay(1_000 ) // auto-advances without delay due to runBlockingTest
81- .. .
83+ // ...
8284}
8385```
8486
@@ -94,10 +96,10 @@ the first [delay].
9496
9597``` kotlin
9698@Test
97- fun testFoo_withLaunch () = runBlockingTest {
99+ fun testFooWithLaunch () = runBlockingTest {
98100 foo()
99101 // the coroutine launched by foo() is completed before foo() returns
100- .. .
102+ // ...
101103}
102104
103105fun CoroutineScope.foo () {
@@ -124,12 +126,12 @@ To control time in the test you can use the [DelayController] interface. The blo
124126
125127``` kotlin
126128@Test
127- fun testFoo_withLaunch_andDelay () = runBlockingTest {
129+ fun testFooWithLaunchAndDelay () = runBlockingTest {
128130 foo()
129131 // the coroutine launched by foo has not completed here, it is suspended waiting for delay(1_000)
130132 advanceTimeBy(1_000 ) // progress time, this will cause the delay to resume
131133 // foo() coroutine launched by foo has completed here
132- .. .
134+ // ...
133135}
134136
135137suspend fun CoroutineScope.foo () {
@@ -142,7 +144,8 @@ suspend fun CoroutineScope.foo() {
142144```
143145
144146* Note:* ` runBlockingTest ` will always attempt to auto-progress time until all coroutines are completed just before
145- exiting. This is a convenience to avoid having to call [ advanceUntilIdle] as the last line of many common test cases.
147+ exiting. This is a convenience to avoid having to call [ advanceUntilIdle] [ DelayController.advanceUntilIdle ]
148+ as the last line of many common test cases.
146149If any coroutines cannot complete by advancing time, a [ UncompletedCoroutinesError] is thrown.
147150
148151### Testing ` withTimeout ` using ` runBlockingTest `
@@ -155,18 +158,18 @@ example an uncompleted `Deferred<Foo>` is provided to the function under test vi
155158
156159``` kotlin
157160@Test(expected = TimeoutCancellationException ::class )
158- fun testFoo_withTimeout () {
161+ fun testFooWithTimeout () {
159162 val uncompleted = CompletableDeferred <Foo >() // this Deferred<Foo> will never complete
160163 foo(uncompleted)
161164 advanceTimeBy(1_000 ) // advance time, which will cause the timeout to throw an exception
162- .. .
165+ // ...
163166}
164167
165168fun CoroutineScope.foo (resultDeferred : Deferred <Foo >) {
166169 launch {
167170 withTimeout(1_000 ) {
168171 resultDeferred.await() // await() will suspend forever waiting for uncompleted
169- .. .
172+ // ...
170173 }
171174 }
172175}
@@ -181,15 +184,15 @@ create a second coroutine.
181184The eager execution of ` launch ` and ` async ` bodies makes many tests easier, but some tests need more fine grained
182185control of coroutine execution.
183186
184- To disable eager execution, you can call [ pauseDispatcher] to pause the [ TestCoroutineDispatcher ] that [ runBlockingTest ]
185- uses.
187+ To disable eager execution, you can call [ pauseDispatcher] [ DelayController.pauseDispatcher ]
188+ to pause the [ TestCoroutineDispatcher ] that [ runBlockingTest ] uses.
186189
187190When the dispatcher is paused, all coroutines will be added to a queue instead running. In addition, time will never
188191auto-progress due to ` delay ` on a paused dispatcher.
189192
190193``` kotlin
191194@Test
192- fun testFoo_withPauseDispatcher () = runBlockingTest {
195+ fun testFooWithPauseDispatcher () = runBlockingTest {
193196 pauseDispatcher {
194197 foo()
195198 // the coroutine started by foo has not run yet
@@ -198,7 +201,7 @@ fun testFoo_withPauseDispatcher() = runBlockingTest {
198201 advanceTimeBy(1_000 ) // progress time, this will cause the delay to resume
199202 // the coroutine started by foo has called println(2) and has completed here
200203 }
201- .. .
204+ // ...
202205}
203206
204207fun CoroutineScope.foo () {
@@ -216,8 +219,8 @@ non-trivial external dependencies and side effects in their launch body.
216219
217220* Important:* When passed a lambda block, ` pauseDispatcher ` will resume eager execution immediately after the block.
218221This will cause time to auto-progress if there are any outstanding ` delay ` calls that were not resolved before the
219- ` pauseDispatcher ` block returned. In advanced situations tests can call [ pauseDispatcher] [ pausedispatcher.noarg ] without a lambda block and
220- then explicitly resume the dispatcher with [ resumeDispatcher] .
222+ ` pauseDispatcher ` block returned. In advanced situations tests can call [ pauseDispatcher] [ DelayController.pauseDispatcher ]
223+ without a lambda block and then explicitly resume the dispatcher with [ resumeDispatcher ] [ DelayController. resumeDispatcher] .
221224
222225## Integrating tests with structured concurrency
223226
@@ -241,6 +244,7 @@ By providing [TestCoroutineScope] a test case is able to control execution of co
241244uncaught exceptions thrown by coroutines are converted into test failures.
242245
243246### Providing ` TestCoroutineScope ` from ` runBlockingTest `
247+
244248In simple cases, tests can use the [ TestCoroutineScope] created by [ runBlockingTest] directly.
245249
246250``` kotlin
@@ -251,21 +255,22 @@ fun testFoo() = runBlockingTest {
251255
252256fun CoroutineScope.foo () {
253257 launch { // CoroutineScope for launch is the TestCoroutineScope provided by runBlockingTest
254- .. .
258+ // ...
255259 }
256260}
257261```
258262
259263This style is preferred when the ` CoroutineScope ` is passed through an extension function style.
260264
261265### Providing an explicit ` TestCoroutineScope `
266+
262267In many cases, the direct style is not preferred because [ CoroutineScope] may need to be provided through anther means
263268such as dependency injection or service locators.
264269
265270Tests can declare a [ TestCoroutineScope] explicitly in the class to support these use cases.
266271
267272Since [ TestCoroutineScope] is stateful in order to keep track of executing coroutines and uncaught exceptions, it is
268- important to ensure that [ cleanupTestCoroutines] [ cleanupTestCoroutines.scope ] is called after every test case.
273+ important to ensure that [ cleanupTestCoroutines] [ TestCoroutineScope.cleanupTestCoroutines ] is called after every test case.
269274
270275``` kotlin
271276class TestClass {
@@ -304,6 +309,7 @@ test libraries to provide library specific integrations. For example, a JUnit4 `
304309[ Dispatchers.setMain] [ setMain ] then expose [ TestCoroutineScope] for use in tests.
305310
306311### Providing an explicit ` TestCoroutineDispatcher `
312+
307313While providing a [ TestCoroutineScope] is slightly preferred due to the improved uncaught exception handling, there are
308314many situations where it is easier to provide a [ TestCoroutineDispatcher] . For example [ Dispatchers.setMain] [ setMain ]
309315does not accept a [ TestCoroutineScope] and requires a [ TestCoroutineDispatcher] to control coroutine execution in
@@ -319,7 +325,7 @@ when the class under test allows a test to provide a [CoroutineDispatcher] but d
319325[ CoroutineScope] .
320326
321327Since [ TestCoroutineDispatcher] is stateful in order to keep track of executing coroutines, it is
322- important to ensure that [ cleanupTestCoroutines] [ cleanupTestCoroutines.dispatcher ] is called after every test case.
328+ important to ensure that [ cleanupTestCoroutines] [ TestCoroutineDispatcher.cleanupTestCoroutines ] is called after every test case.
323329
324330``` kotlin
325331class TestClass {
@@ -363,7 +369,7 @@ this to provide alternatives to `runBlockingTest`.
363369
364370``` kotlin
365371@Test
366- fun testFoo_withAutoProgress () {
372+ fun testFooWithAutoProgress () {
367373 val scope = TestCoroutineScope ()
368374 scope.foo()
369375 // foo is suspended waiting for time to progress
@@ -381,6 +387,7 @@ fun CoroutineScope.foo() {
381387```
382388
383389## Using time control with ` withContext `
390+
384391Calls to ` withContext(Dispatchers.IO) ` or ` withContext(Dispatchers.Default) ` are common in coroutines based codebases.
385392Both dispatchers are not designed to interact with ` TestCoroutineDispatcher ` .
386393
@@ -413,37 +420,34 @@ delays, or when execution control is needed to test complex logic.
413420### Status of the API
414421
415422This API is experimental and it is may change before migrating out of experimental (while it is marked as
416- ` @ExperimentalCoroutinesApi ` ). Changes during experimental may have deprecation applied when possible, but it is not
417- advised to use the API before it leaves experimental due to possible breaking changes.
423+ [ ` @ExperimentalCoroutinesApi ` ] [ ExperimentalCoroutinesApi ] ).
424+ Changes during experimental may have deprecation applied when possible, but it is not
425+ advised to use the API in stable code before it leaves experimental due to possible breaking changes.
418426
419427If you have any suggestions for improvements to this experimental API please share them them on the
420428[ issue tracker] ( https://github.com/Kotlin/kotlinx.coroutines/issues ) .
421429
422-
423430<!-- - MODULE kotlinx-coroutines-core -->
424431<!-- - INDEX kotlinx.coroutines -->
425432[ Dispatchers.Main ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html
426433[ CoroutineDispatcher ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html
427- [ CoroutineScope ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/
428434[ launch ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html
429435[ async ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html
430436[ delay ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html
437+ [ CoroutineScope ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
438+ [ ExperimentalCoroutinesApi ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-experimental-coroutines-api/index.html
431439<!-- - MODULE kotlinx-coroutines-test -->
432440<!-- - INDEX kotlinx.coroutines.test -->
433441[ setMain ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/kotlinx.coroutines.-dispatchers/set-main.html
434-
435- [ runBlockingTest ] : https://notapage/
436- [ UncompletedCoroutinesError ] : https://notapage/
437- [ DelayController ] : https://notapage/
438- [ pauseDispatcher ] : https://notapage/
439- [ pauseDispatcher.noarg ] : https://notapage/noarg
440- [ resumeDispatcher ] : https://notapage/
441- [ advanceUntilIdle ] : https://notapage/
442- [ advanceTimeBy ] : https://notapage/
443- [ resumeDispatcher ] : https://notapage/
444- [ TestCoroutineDispatcher ] : https://notapage/
445- [ TestCoroutineExceptionHandler ] : https://notapage/
446- [ TestCoroutineScope ] : https://notapage/
447- [ cleanupTestCoroutines.scope ] : https://notapage/cleanupscope
448- [ cleanupTestCoroutines.dispatcher ] : https://notapage/cleanupdispatcher
442+ [ runBlockingTest ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-blocking-test.html
443+ [ UncompletedCoroutinesError ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-uncompleted-coroutines-error/index.html
444+ [ DelayController ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/index.html
445+ [ DelayController.advanceUntilIdle ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/advance-until-idle.html
446+ [ DelayController.pauseDispatcher ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/pause-dispatcher.html
447+ [ TestCoroutineDispatcher ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-dispatcher/index.html
448+ [ DelayController.resumeDispatcher ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/resume-dispatcher.html
449+ [ TestCoroutineScope ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scope/index.html
450+ [ TestCoroutineExceptionHandler ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-exception-handler/index.html
451+ [ TestCoroutineScope.cleanupTestCoroutines ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scope/cleanup-test-coroutines.html
452+ [ TestCoroutineDispatcher.cleanupTestCoroutines ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-dispatcher/cleanup-test-coroutines.html
449453<!-- - END -->
0 commit comments