Dispatch

Dispatch embeds CoroutineDispatcher configurations into a CoroutineContext, and uses extension functions to reference those configurations when creating new coroutines.  This allows for boilerplate-free CoroutineDispatcher "injection".  

class MyClass @Inject constructor(
  val coroutineScope : MainCoroutineScope
) {
  fun doWork(): Job {
    return launch {
      // on the "Main" dispatcher, or Dispatchers.Main by default
      withIO {
        // now on the "IO" dispatcher, or Dispatchers.IO by default
      }
    }
  }
}


It is very simple to utilize normal dispatchers in production code, but swap them out for custom test dispatchers:

// `testProvided` adds a TestDispatcherProvider to a TestCoroutineScope
@Test fun `my test`() = testProvided {
  val subject = MyClass(this) // inject the test scope
  subject.doWork()
  // both the "Main" and "IO" code execute sequentially,
  // or single-threaded, using a TestCoroutineDispatcher
}