Skip to content

Kotlin Coroutines

Praneeth edited this page Mar 17, 2023 · 1 revision

Kotlin coroutines

The team leverages Kotlin coroutines for background processing, however we never launch a new dispatcher or use any of the built-in dispatchers. Instead, we use one of our existing dispatchers:

  • BackgroundDispatcher: for executing expensive operations
  • BlockingDispatcher: for executing operations that need to be done in parallel (generally, don't use this--prefer InMemoryBlockingCache, instead)

New operations should create a separate scope using one of the existing dispatchers & perform their execution using that. Note that failures in operations will cause the scope itself to enter a failed state, so scopes shouldn't be kept long-term for correctness.

Synchronizing state in tests

One major benefit in consolidating all execution on the same coroutine dispatchers is that facilitates easy thread synchronization boundaries in tests. TestCoroutineDispatchers is a test-injectable utility with a number of useful API functions:

  • (un)registerIdlingResource: ensures background operations finish automatically before performing Espresso UI interactions
  • runCurrent: run all background tasks that can be completed now without advancing the clock (Robolectric tests run with a fake clock that has to be manually advanced)
  • advanceTimeBy / advanceUntilIdle: functions for running tasks scheduled in the future

Generally, registering an idling resource for shared Espresso/Robolectric tests and calling runCurrent after performing any operations in the bodies of tests is sufficient to guarantee no test flakes for nearly all scenarios. There are many examples of using both throughout the codebase.

advanceTimeBy/advanceUntilIdle should only be used in cases where they are specifically needed (prefer runCurrent where possible since advanceUntilIdle is more of a "sledgehammer" solution and should rarely be necessary).

Clone this wiki locally