Olivier Patry
06/28/2023, 9:54 AMViewModel
ran on JVM.
Exception while trying to handle coroutine exception
IllegalStateException: Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used
CoroutinesInternalError: Fatal exception in coroutines machinery for DispatchedContinuation[<http://Dispatchers.IO|Dispatchers.IO>, Continuation at com.myscript.nebo.dms.MyViewModel$1$1.invokeSuspend(CoroutinesIssueTest.kt)@52fe5814]. Please read KDoc to 'handleFatalException' method and report this incident to maintainers
(see complete stack trace in thread).
I extracted a small repro unrelated to business logic but representative of my use case.Olivier Patry
06/28/2023, 9:55 AMOlivier Patry
06/28/2023, 9:55 AMOlivier Patry
06/28/2023, 9:56 AMOlivier Patry
06/28/2023, 9:56 AMinit {}
there isn't any issue anymore.Olivier Patry
06/28/2023, 9:59 AMUnconfinedTestDispatcher
by StandardTestDispatcher
in setMain, it's the same.
If I inject a work dispatcher StandardTestDispatcher
as a replacement of <http://Dispatchers.IO|Dispatchers.IO>
, it works.Olivier Patry
06/28/2023, 10:00 AM<http://Dispatchers.IO|Dispatchers.IO>
by StandardTestDispatcher
Olivier Patry
06/28/2023, 11:06 AMephemient
06/28/2023, 11:15 AMviewModelScope
isn't well testable, https://github.com/Kotlin/kotlinx.coroutines/issues/3298 and other issuesephemient
06/28/2023, 11:18 AMviewModelScope
is not the lifecycle you want. either use viewLifecycleOwner.lifecycleScope
from outside the ViewModel, or something scoped to the application lifecycleOlivier Patry
06/28/2023, 11:24 AMviewModelScope
isn't considered the good choice?ephemient
06/28/2023, 11:38 AMsuspend fun
that the UI can launch from its own lifecycle scope is fine, and you can use the standard runTest
function. if it's really supposed to be some long-running background operation, then you should maybe reconsider the approach (e.g. WorkManager implementation) but regardless, inject the thing handling background workOlivier Patry
07/04/2023, 5:49 PMcustomCoroutineScope
(CoroutineScope(UnconfinedTestDispatcher())
) in my ViewModel
ctor (if none provided, falls back to AndroidX viewModelScope
) and situation highly improved indeed đŸ¤¯.
I didn't expect that the issue came from viewModelScope
in test context.
Thanks @ephemient for this enlightening explanation/hint đŸ™‡.
(was too invasive to change the design of the public API under test like suggested)ephemient
07/04/2023, 9:12 PMrunTest
, there's backgroundScope which you can use instead of CoroutineScope()
with the benefit that failures inside that scope get reportedOlivier Patry
07/05/2023, 7:22 AMOlivier Patry
07/05/2023, 7:24 AMbackgroundScope
right now, now need to understand why)chanjungskim
07/20/2023, 3:08 PM