• Home
Name
Date
Size
#Lines
LOC

..--

resources/META-INF/03-May-2024-129

src/03-May-2024-691342

test/03-May-2024-949771

README.mdD03-May-202420.2 KiB458358

build.gradleD03-May-202473 43

README.md

1 # Module kotlinx-coroutines-test
2 
3 Test utilities for `kotlinx.coroutines`.
4 
5 This package provides testing utilities for effectively testing coroutines.
6 
7 ## Using in your project
8 
9 Add `kotlinx-coroutines-test` to your project test dependencies:
10 ```
11 dependencies {
12     testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.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
21 test in situations where the platform `Main` dispatcher is not available, or you wish to replace `Dispatchers.Main` with a
22 testing dispatcher.
23 
24 Once 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 
28 You can override the `Main` implementation using [setMain][setMain] method with any [CoroutineDispatcher] implementation, e.g.:
29 
30 ```kotlin
31 
32 class 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 ```
55 Calling `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 
60 To test regular suspend functions or coroutines started with `launch` or `async` use the [runBlockingTest] coroutine
61 builder that provides extra test control to coroutines.
62 
63 1. Auto-advancing of time for regular suspend functions
64 2. Explicit time control for testing multiple coroutines
65 3. Eager execution of `launch` or `async` code blocks
66 4. Pause, manually advance, and restart the execution of coroutines in a test
67 5. Report uncaught exceptions as test failures
68 
69 ### Testing regular suspend functions
70 
71 To test regular suspend functions, which may have a delay, you can use the [runBlockingTest] builder to start a testing
72 coroutine. Any calls to `delay` will automatically advance virtual time by the amount delayed.
73 
74 ```kotlin
75 @Test
76 fun testFoo() = runBlockingTest { // a coroutine with an extra test control
77     val actual = foo()
78     // ...
79 }
80 
81 suspend 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 
91 Inside of [runBlockingTest], both [launch] and [async] will start a new coroutine that may run concurrently with the
92 test case.
93 
94 To make common testing situations easier, by default the body of the coroutine is executed *eagerly* until
95 the first call to [delay] or [yield].
96 
97 ```kotlin
98 @Test
99 fun testFooWithLaunch() = runBlockingTest {
100     foo()
101     // the coroutine launched by foo() is completed before foo() returns
102     // ...
103 }
104 
105 fun 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 
113 suspend fun bar() {}
114 ```
115 
116 `runBlockingTest` will auto-progress virtual time until all coroutines are completed before returning. If any coroutines
117 are 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 
123 If the coroutine created by `launch` or `async` calls `delay` then the [runBlockingTest] will not auto-progress time
124 right away. This allows tests to observe the interaction of multiple coroutines with different delays.
125 
126 To 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
131 fun 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 
139 suspend 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
149 exiting. This is a convenience to avoid having to call [advanceUntilIdle][DelayController.advanceUntilIdle]
150 as the last line of many common test cases.
151 If any coroutines cannot complete by advancing time, an [UncompletedCoroutinesError] is thrown.
152 
153 ### Testing `withTimeout` using `runBlockingTest`
154 
155 Time 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 
158 Depending on the code, causing the code to suspend may need to use different mocking or fake techniques. For this
159 example an uncompleted `Deferred<Foo>` is provided to the function under test via parameter injection.
160 
161 ```kotlin
162 @Test(expected = TimeoutCancellationException::class)
163 fun 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 
170 fun 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
181 call to `withTimeout` is in a regular suspend function, consider calling `launch` or `async` inside your test body to
182 create a second coroutine.
183 
184 ### Using `pauseDispatcher` for explicit execution of `runBlockingTest`
185 
186 The eager execution of `launch` and `async` bodies makes many tests easier, but some tests need more fine grained
187 control of coroutine execution.
188 
189 To disable eager execution, you can call [pauseDispatcher][DelayController.pauseDispatcher]
190 to pause the [TestCoroutineDispatcher] that [runBlockingTest] uses.
191 
192 When the dispatcher is paused, all coroutines will be added to a queue instead running. In addition, time will never
193 auto-progress due to `delay` on a paused dispatcher.
194 
195 ```kotlin
196 @Test
197 fun 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 
209 fun 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 
218 Using `pauseDispatcher` gives tests explicit control over the progress of time as well as the ability to enqueue all
219 coroutines. As a best practice consider adding two tests, one paused and one eager, to test coroutines that have
220 non-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.
223 This 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]
225 without a lambda block and then explicitly resume the dispatcher with [resumeDispatcher][DelayController.resumeDispatcher].
226 
227 ## Integrating tests with structured concurrency
228 
229 Code 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
231 classes 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 
245 By providing [TestCoroutineScope] a test case is able to control execution of coroutines, as well as ensure that
246 uncaught exceptions thrown by coroutines are converted into test failures.
247 
248 ### Providing `TestCoroutineScope` from `runBlockingTest`
249 
250 In simple cases, tests can use the [TestCoroutineScope] created by [runBlockingTest] directly.
251 
252 ```kotlin
253 @Test
254 fun testFoo() = runBlockingTest {
255     foo() // runBlockingTest passed in a TestCoroutineScope as this
256 }
257 
258 fun CoroutineScope.foo() {
259     launch {  // CoroutineScope for launch is the TestCoroutineScope provided by runBlockingTest
260         // ...
261     }
262 }
263 ```
264 
265 This style is preferred when the `CoroutineScope` is passed through an extension function style.
266 
267 ### Providing an explicit `TestCoroutineScope`
268 
269 In many cases, the direct style is not preferred because [CoroutineScope] may need to be provided through another means
270 such as dependency injection or service locators.
271 
272 Tests can declare a [TestCoroutineScope] explicitly in the class to support these use cases.
273 
274 Since [TestCoroutineScope] is stateful in order to keep track of executing coroutines and uncaught exceptions, it is
275 important to ensure that [cleanupTestCoroutines][TestCoroutineScope.cleanupTestCoroutines] is called after every test case.
276 
277 ```kotlin
278 class TestClass {
279     private val testScope = TestCoroutineScope()
280     private lateinit var subject: Subject = null
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 
300 class 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
310 test 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 
315 While providing a [TestCoroutineScope] is slightly preferred due to the improved uncaught exception handling, there are
316 many situations where it is easier to provide a [TestCoroutineDispatcher]. For example [Dispatchers.setMain][setMain]
317 does not accept a [TestCoroutineScope] and requires a [TestCoroutineDispatcher] to control coroutine execution in
318 tests.
319 
320 The main difference between `TestCoroutineScope` and `TestCoroutineDispatcher` is how uncaught exceptions are handled.
321 When 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 
325 A test can use a `TestCoroutineDispatcher` without declaring an explicit `TestCoroutineScope`. This is preferred
326 when the class under test allows a test to provide a [CoroutineDispatcher] but does not allow the test to provide a
327 [CoroutineScope].
328 
329 Since [TestCoroutineDispatcher] is stateful in order to keep track of executing coroutines, it is
330 important to ensure that [cleanupTestCoroutines][DelayController.cleanupTestCoroutines] is called after every test case.
331 
332 ```kotlin
333 class 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 
355 fun 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
363 to test failures. However, exposing a `CoroutineScope` to callers of a function may lead to complicated code, in which
364 case this is the preferred pattern.
365 
366 ### Using `TestCoroutineScope` and `TestCoroutineDispatcher` without `runBlockingTest`
367 
368 It is supported to use both [TestCoroutineScope] and [TestCoroutineDispatcher] without using the [runBlockingTest]
369 builder. Tests may need to do this in situations such as introducing multiple dispatchers and library writers may do
370 this to provide alternatives to `runBlockingTest`.
371 
372 ```kotlin
373 @Test
374 fun 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 
382 fun 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 
393 Calls to `withContext(Dispatchers.IO)` or `withContext(Dispatchers.Default)` are common in coroutines based codebases.
394 Both dispatchers are not designed to interact with `TestCoroutineDispatcher`.
395 
396 Tests should provide a `TestCoroutineDispatcher` to replace these dispatchers if the `withContext` calls `delay` in the
397 function under test. For example, a test that calls `veryExpensiveOne` should provide a `TestCoroutineDispatcher` using
398 either dependency injection, a service locator, or a default parameter.
399 
400 ```kotlin
401 suspend fun veryExpensiveOne() = withContext(Dispatchers.Default) {
402     delay(1_000)
403     1 // for very expensive values of 1
404 }
405 ```
406 
407 In situations where the code inside the `withContext` is very simple, it is not as important to provide a test
408 dispatcher. 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
410 directly, there is no need to inject a `TestCoroutineDispatcher` into this function.
411 
412 ```kotlin
413 suspend fun veryExpensiveTwo() = withContext(Dispatchers.Default) {
414     2 // for very expensive values of 2
415 }
416 ```
417 
418 Tests should provide a `TestCoroutineDispatcher` to code that calls `withContext` to provide time control for
419 delays, or when execution control is needed to test complex logic.
420 
421 
422 ### Status of the API
423 
424 This API is experimental and it is may change before migrating out of experimental (while it is marked as
425 [`@ExperimentalCoroutinesApi`][ExperimentalCoroutinesApi]).
426 Changes during experimental may have deprecation applied when possible, but it is not
427 advised to use the API in stable code before it leaves experimental due to possible breaking changes.
428 
429 If 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