Can't capture every emittion from StateFlow when testing with Turbine
14:17 27 Dec 2025

I'm trying to test my viewmodels that exposes a public StateFlow via stateIn operator:

    private val _uiState: MutableStateFlow =
        MutableStateFlow(createInitialState(savedStateHandle))

    val uiState: StateFlow = _uiState
        .onStart { onViewSubscribed() }
        .stateIn(
            scope = viewModelScope,
            started = sharingStarted,
            initialValue = _uiState.value
        )

I'm using turbine to capture the emittions and assert on them:

    @Test
    fun `Test that tries to capture each emittion separately`() = runTest {

        viewModel.stateFlow.test {
            val initial = awaitItem()
            assertEquals(initial.isLoading, false)

            viewModel.setStates()
            val loading = awaitItem()
            assertEquals(loading.isLoading, true)
            val loaded = awaitItem()
            assertEquals(loaded.isLoading, false)
            val loadingAgain = awaitItem()
            assertEquals(loadingAgain.isLoading, true)
            val loadedAgain = awaitItem()
            assertEquals(loadedAgain.isLoading, false)
            val loadingAgain2 = awaitItem()
            assertEquals(loadingAgain2.isLoading, true)
        }

    }


That tests this function:

    fun setStates() {
        viewModelScope.launch {
            updateStateFlow { copy(isLoading = true) }
            updateStateFlow { copy(isLoading = false) }
            updateStateFlow { copy(isLoading = true) }
            updateStateFlow { copy(isLoading = false) }
            updateStateFlow { copy(isLoading = true) }
        }
    }


And I'm overriding the main dispatcher in tests via this JUnit5 Extension :

@ExperimentalCoroutinesApi
class MainDispatcherExtension(
    private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) : BeforeEachCallback, AfterEachCallback {
    override fun beforeEach(context: ExtensionContext?) {
        Dispatchers.setMain(testDispatcher)
    }

    override fun afterEach(context: ExtensionContext?) {
        Dispatchers.resetMain()
    }
}

The problem is I can not capture each and every emittion separately, Turbine throws No value produced in 3s exception (It actually captures only the last emittion so the others waiting for an emittions cause a timeout, naturally.). But if I add delay between emittions I can capture them as i want (separately and each has different assertions) :

    fun setStates() {
        viewModelScope.launch {
            updateStateFlow { copy(isLoading = true) }
            delay(1)
            updateStateFlow { copy(isLoading = false) }
            delay(1)
            updateStateFlow { copy(isLoading = true) }
            delay(1)
            updateStateFlow { copy(isLoading = false) }
            delay(1)
            updateStateFlow { copy(isLoading = true) }
        }
    }

But as you see, this is changing the prod code just to be able to test it and it is disgusting, not a viable option.

I read this article about "conflation" of stateflow and tried to apply its solution by changing my runTest dispatcher to StandardTestDispatcher:

@ExperimentalCoroutinesApi
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 thus, according to article Turbine would collect in UnconfinedTestDispatcher and i need to be able to capture each emittion but it did not work. What is the reason ? Can you clarify this for me ? Maybe I can tag the author of the article to see if I misunderstood some part of it.

android kotlin unit-testing kotlin-flow turbine