I have a ViewModel that uses StateFlow.asLiveData() to expose a Repository class's StateFlow items and I'm trying to write a test for the ViewModel. My tests are configured with a Mock of an Observer<LoadingStatus> on the ViewModel's exposed LiveData.
The code I'm testing calls this method to update its loading status:
suspend fun MutableStateFlow<LoadingStatus>.performWithStatusUpdates(operation: suspend () -> Unit) {
this.value = LoadingStatus.Loading()
try {
operation.invoke()
this.value = LoadingStatus.Success()
} catch (e: Throwable) {
this.value = LoadingStatus.Error(e)
}
}
My tests look something like this:
fun testSomething() = runTest {
viewModel.doSomething()
advanceUntilIdle()
argumentCaptor<LoadingStatus>().apply {
verify(loadingStatusObserver, atLeast(2)).onChanged(capture())
assertTrue(allValues.any { it is LoadingStatus.Loading })
assertTrue(allValues.any { it is LoadingStatus.Success })
}
}
The ViewModel contains code like this:
val loadingStatus = repository.loadingStatusObservable.asLiveData(
viewModelScope.coroutineContext + ioDispatcher
) // when running tests, ioDispatcher is a StandardTestDispatcher passed into the viewModel
fun doSomething() {
viewModelScope.launch(ioDispatcher) {
repository.doSomething()
}
}
And the repository does something like this:
val loadingStatusObservable = MutableStateFlow<LoadingStatus>(LoadingStatus.Idle())
suspend fun doSomething() {
loadingStatusObservable.performWithStatusUpdates {
apiService.doSomethingElse()
}
}
The repository has similar tests that call doSomething()
and verify that the status goes to loading and then success, and they pass, but the view model ones fail to pick up the Loading status. If I comment out the line in performWithStatusUpdates
that sets the status to success after the operation, the tests do pick up the Loading status, so I'm convinced it's something to do with timing. I have run this code with print statements and debuggers and verified that the status is updating correctly, but the change isn't getting picked up by the observers.
How can I make an observer on a StateFlow.asLiveData() detect all changes, even when they're quickly followed by another, different change?