Trying to test my coroutines with unit tests (JUni...
# coroutines
a
Trying to test my coroutines with unit tests (JUnit Mockito) and referring kotlin coroutines repo i was using runBlockingTest and it works as expected to advance the execution . But when I run tests concurrently in a suite they are flacky as they are sharing one dispatcher (my assumption) in view model. When looking at the repo there is TestCoroutineDispatcher but it looks like the API to replace actual dispatcher with that one is only .setMain which replaces Dispatchers.Main. I am using Dispatchers.IO so the mentioned api seems of no use . Is there a way to test coroutines with this set up without injecting the dispatcher into view model? @Test fun `test loadMenu calls interactor getMenu`() = runBlockingTest { viewModel.loadMenu() Mockito.verify(interactor).getMenu() } fun loadMenu() { viewModelScope.launch(Dispatchers.IO) { try { interactor.getMenu().menuDetails.menu.run { updateMainCatList(this) } } catch (ex: Throwable) { //todo handle exceptions } } }
s
There is no equivalent setIO or setDefault for the IO and the Default dispatchers, like setMain.... Usually, I inject the appropriate CoroutineContext(= Dispatcher) into my ViewModels (and Interactors/UseCases/Repos/etc), allowing them to easily used in tests, injecting a TestCoroutineDispatcher, having full control over (virtual) passage of time.
Still, using Dispatcher,IO should work in your case. I think your code has uncontrolled side-effects.
b
@streetsofboston Why do you think it should work? In example launch called on viewModelScope, and it is not affected by outer testCoroutineScope
s
Depends on what was meant by 'flaky'. I assumed it was race conditions due to multiple threads in Dispatchers.IO . They also could be flaky due to the fact that runBlocking ends too soon, since it only blocks until all tasks/Coroutines are finished in its TestCoroutineScope. It won't wait for all the tasks/Coroutines to finish in the viewModelScope.
b
It's flaky not because of multiple threads in IO, but just because it's not Main - so 'viewModel.loadMenu()' may end before launch coroutine finished - so there is a rc between io and runBlockingTest's dispatcher
👍 1
a
That was my assumption ☝️
By flaky I mean it passes every time when I run it as a single test 30 times but running the suite 30 times give 5-6 failed times
I can see how injecting will "fix" it but imo there should be setIo the same as setMain etc , what do you think?
b
afaik it was designed that way to not replace default dispatchers as we do that with RxJava
a
So it is not just me 😂 thanks I will read that, so for me to "fix" the flakiness right away the only way is to inject and then replace in the test ? That is what it seems like now ...
b
I believe even this can not work in some cases - e.g. if your repository uses predefined dispatchers (if they differ from injected to VM) in withContext for example, result will be the same. But if your tests as simple as you described initially, that should work.
a
The above test is the simplest one of course, I understand what the issue is with the dispatcher, so the only way is to inject with di the same instance of dispatcher to repo and viewmodel to replace it?
b
yep, seems so