Hi I've some trouble when testing `StateFlow` s in...
# android
s
Hi I've some trouble when testing
StateFlow
s in
ViewModel
s. First of all i've created a SO question and a min. reproducable example repo for this. If you want you can proceed with there : https://stackoverflow.com/questions/79855932/cant-capture-every-emittion-from-stateflow-when-testing-with-turbine If you prefer read from here i'll post in this post's thread.
My problem with this topic is that I can not capture each and every emission from a
StateFlow
that is created with
stateIn
operator in a `ViewModel`:
Copy code
class CounterViewModel : ViewModel() {

    private val _uiState = MutableStateFlow(CounterUiState())
    val uiState: StateFlow<CounterUiState> = _uiState
        .onStart { onViewSubscribed() }
        .stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000),
        initialValue = _uiState.value
    )
    ...
}
Not a real life example but think of this scenario:
Copy code
fun incrementAsync() {
    viewModelScope.launch {
        _uiState.update { it.copy(isLoading = true) }
        _uiState.update { it.copy(isLoading = false) }
        _uiState.update { it.copy(isLoading = true) }
        delay(1000)
        _uiState.update { it.copy(count = it.count + 1, isLoading = false) }
    }
}
I want to be able capture each and every emission but with my current setup this is not possible and I can not really understand why. I read this article from @zsmb about StateFlow's conflation: https://zsmb.co/conflating-stateflows/ But The Moment I use
stateIn
operator via
viewModelScope
it started not to work again. My JUnit5 dispatcher extension to override mainDispatcher is like this :
Copy code
@OptIn(ExperimentalCoroutinesApi::class)
class MainDispatcherExtension(
    private val testDispatcher: TestDispatcher = StandardTestDispatcher()
) : BeforeEachCallback, AfterEachCallback {

    override fun beforeEach(context: ExtensionContext?) {
        Dispatchers.setMain(testDispatcher)
    }

    override fun afterEach(context: ExtensionContext?) {
        Dispatchers.resetMain()
    }
}
And my test case for the above function is like this :
Copy code
@Test
@DisplayName("incrementAsync shows loading then increments")
fun `incrementAsync shows loading then increments`() = runTest {
    viewModel.uiState.test {
        assertThat(awaitItem().count).isEqualTo(0)

        viewModel.incrementAsync()

        val loading = awaitItem()
        assertThat(loading.isLoading).isTrue()

        val notLoading = awaitItem()
        assertThat(notLoading.isLoading).isFalse()


        val loading2 = awaitItem()
        assertThat(loading2.isLoading).isTrue()

        val done = awaitItem()
        assertThat(done.isLoading).isFalse()
        assertThat(done.count).isEqualTo(1)
    }
}
How can I solve this ? Any ideas ?