https://kotlinlang.org logo
d

Dmitry

01/03/2023, 1:53 PM
Hello All, I am working on a test case for
ViewModel
classes with the recent coroutines-test API and it doesn't work as expected.
Copy code
@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)
Copy code
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()")
    }
The issue was caused by lack of replacement for Dispatchers.IO. In the code above I overrided the Main dispatcher, but it does not replace the dispatcher which used in
.flowOn()
It have to replaced explicitly in addition to the code above.
5 Views