Sevban Bayir
12/28/2025, 4:28 PMStateFlow 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.Sevban Bayir
12/28/2025, 5:37 PMStateFlow that is created with stateIn operator in a `ViewModel`:
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:
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 :
@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 :
@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 ?