You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-coroutine-dispatcher/immediate.html states that, if Dispatchers.Main.immediate is supported, it behaves like Dispatchers.Unconfined when already in the correct execution context; otherwise, it should throw UnsupportedOperationException. However, this is not the case for Dispatchers.setMain, which will just use the dispatcher itself for Dispatchers.Main.immediate, without preventing stack overflows or eager execution of coroutines.
This causes real problems in the test code. The main example is viewModelScope, a CoroutineScope that corresponds to a ViewScope, Android-specific storage of data for use in UI. Because of being UI-related, viewModelScope uses the UI thread by default, and to avoid unnecessary dispatches when working with a ViewModel from the main thread, it uses Dispatchers.Main.immediate.
Code that tests functions using viewModelScope is typically structured in a way that expects the dispatches to happen eagerly—corresponding to real-world behavior in the typical case—which necessitates the use of UnconfinedTestDispatcher in Dispatchers.setMain for such tests. However, this will lead to incorrect dispatching behavior for just Dispatchers.Main, which is supposed to behave like a StandardTestDispatcher.
The most straightforward idea is two-part:
1. Make StandardTestDispatcher a MainCoroutineDispatcher, confined to the thread and exposing a proper immediate.
Problems:
What is the test thread? If we just check the thread at the time of creation of the test dispatcher, can this can cause problems with multithreaded test runners?
Calls to advance*/runCurrent from other threads will hang if we implement this naively. Maybe a better idea is not to confine the execution to one thread, but to consider the thread doing the innermost advance*/runCurrent to be the "main" thread.
2. Throw UnsupportedOperationException for Dispatchers.Main.immediate for non-MainCoroutineDispatcher arguments to Dispatchers.setMain.
This is a breaking change.
We could lessen the issue by also making UnconfinedTestDispatcher a MainCoroutineDispatcher, but that would promote its incorrect use.
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-coroutine-dispatcher/immediate.html states that, if
Dispatchers.Main.immediate
is supported, it behaves likeDispatchers.Unconfined
when already in the correct execution context; otherwise, it should throwUnsupportedOperationException
. However, this is not the case forDispatchers.setMain
, which will just use the dispatcher itself forDispatchers.Main.immediate
, without preventing stack overflows or eager execution of coroutines.This causes real problems in the test code. The main example is
viewModelScope
, aCoroutineScope
that corresponds to aViewScope
, Android-specific storage of data for use in UI. Because of being UI-related,viewModelScope
uses the UI thread by default, and to avoid unnecessary dispatches when working with aViewModel
from the main thread, it usesDispatchers.Main.immediate
.Code that tests functions using
viewModelScope
is typically structured in a way that expects the dispatches to happen eagerly—corresponding to real-world behavior in the typical case—which necessitates the use ofUnconfinedTestDispatcher
inDispatchers.setMain
for such tests. However, this will lead to incorrect dispatching behavior for justDispatchers.Main
, which is supposed to behave like aStandardTestDispatcher
.The most straightforward idea is two-part:
1. Make
StandardTestDispatcher
aMainCoroutineDispatcher
, confined to the thread and exposing a properimmediate
.Problems:
advance*
/runCurrent
from other threads will hang if we implement this naively. Maybe a better idea is not to confine the execution to one thread, but to consider the thread doing the innermostadvance*
/runCurrent
to be the "main" thread.2. Throw
UnsupportedOperationException
forDispatchers.Main.immediate
for non-MainCoroutineDispatcher
arguments toDispatchers.setMain
.This is a breaking change.
UnconfinedTestDispatcher
aMainCoroutineDispatcher
, but that would promote its incorrect use.Dispatchers.setMain
, we should deprecate it in favor of something else. For example, if we end up implementing Test module shall provide ability to replace Dispatchers.IO and Dispatchers.Default #982, we could just provide something likeDispatchers.mock(main, default)
that would also fix the issue withMain.immediate
.The text was updated successfully, but these errors were encountered: