Dmitry
01/03/2023, 1:53 PMViewModel classes with the recent coroutines-test API and it doesn't work as expected.
@Test
fun `when balanceOf() is called with existing parameter model state is updated with correct value`() = runTest {
Dispatchers.setMain(StandardTestDispatcher())
fakeWalletRepository.setPositiveBalanceOfResponse()
assertThat("Model balance is not default", subj.uiState.value.wallet.getBalance().toInt() == 0)
assertThat("Errors queue is not empty", subj.uiState.value.errors.isEmpty())
assertThat("State is not default", subj.uiState.value.status == Status.NONE)
subj.balanceOf("0x6f1d841afce211dAead45e6109895c20f8ee92f0")
advanceUntilIdle()
assertThat("Model balance is not updated with correct value", subj.uiState.value.wallet.getBalance().toLong() == 42L)
assertThat("Errors queue is not empty", subj.uiState.value.errors.isEmpty())
assertThat("State is not set as BALANCE", subj.uiState.value.status == Status.BALANCE)
}
The issue is not working stably - usually it fails, under debugger usually it passes.
Based on my understanding StandartTestDispatcher shouldn't run coroutines until advanceUntilIdle call when UnconfinedTestDispatcher run them immediately. advanceUntilIdle should wait until all coroutines are finished, but it seems there is a race condition in the next assertThat() call which causes ambiguity in the behaviour of my test case.
advanceUntilIdle should guarantee all coroutines end their work. Does it mean race condition occurs somewhere under .collect{} or state.update {} calls? (In my understanding advanceUntilIdle should wait end of their execution too)
fun balanceOf(owner: String) {
logger.d("[start] balanceOf()")
viewModelScope.launch {
repository.balanceOf(owner)
.flowOn(<http://Dispatchers.IO|Dispatchers.IO>)
.collect { value ->
logger.d("collect get balance result")
processBalanceOfResponse(value)
}
}
logger.d("[end] balanceOf()")
}Dmitry
01/03/2023, 3:44 PM.flowOn() It have to replaced explicitly in addition to the code above.