:wave: I am trying to migrate some of the tests f...
# coroutines
v
đź‘‹ I am trying to migrate some of the tests from
coroutines-test:1.5
to 1.6.1 and can’t wrap my head around some of the new behaviours. In 1.6 we seemingly have lost the ability to control the dispatch of individual dispatchers used internally by the classes under test, since all of the test dispatchers have to use a shared
TestCoroutineScheduler
that now is the owner of the shared virtual time. More details in the thread.
Consider this API as an example:
Copy code
class AsyncWorkRunner(
  private val asyncWorkScope: CoroutineScope,
  private val backgroundDispatcher: CoroutineDispatcher
) {
   ...
  fun scheduleWork() {
    asyncWorkScope.launch(backgroundDispatcher) {
      // someExpensiveWorkThatMightTakeSomeTime()
      workResult.tryEmit(Complete)
    }
  }

  suspend fun waitForWorkToCompleteWithTimeout() {
    if (workResult.value != Complete) { 
       withTimeout(100) { workResult.first { it == Complete } }
    }
  }
}
In 1.5, I could provide 2 independent
TestCoroutineDispatcher
s: one for
runBlockingTest
and another for the
backgroundDispatcher
. This would allow to pause the execution of
backgroundDispatcher
and advance the test dispatcher only, like so:
Copy code
runBlockingTest(testBlockDispatcher) {
    backgroundWorkDispatcher.pauseDispatcher()
    
    workRunner.scheduleWork()
    
    val timeBeforeWait = testBlockDispatcher.currentTime

    workRunner.waitForWorkToCompleteWithTimeout() //testBlockDispatcher auto-advanced due to the use of withTimeout   
    assertThat(testBlockDispatcher.currentTime - timeBeforeWait).isEqualTo(100)
}
This is no longer possible with
coroutines-test:1.6
using any of the test dispatchers since
TestCoroutineScheduler
must be shared between the test block dispatcher and the
backgroundDispatcher
otherwise a
Detected use of different schedulers
error is thrown. If the same scheduler is used,
runCurrent
would advance both of them at the same time which prevents me from testing the timeout behaviour. I was sort of able to mimic the old behaviour by writing my own
CoroutineDispatcher
that never dispatches anything and by swapping it around when needed. But this seems… complicated and too limiting. It feels like I am missing something fundamental. Is there a way of precise control of when the internal dispatchers run with coroutines 1.6?
a
This isn’t directly an equivalent, but could you rework the test code in such a way that is less dependent on the exact behavior of the dispatchers? If you could control
someExpensiveWorkThatMightTakeSomeTime()
, then in one test you could have it delay by
50
, and in another you could have it delay by
150
, and in the latter case you should expect to see the timeout hit.
âž• 1
v
Thanks! Having the actual long-running work extracted and/or configurable should work. Probably will even be a better test - to your point of relying on internal dispatch sequence.
👍 1