Testing Flows

Testing Mid-level Kotlin 2.0+ API 24+ Reviewed By Best Latest Apps

Pick the fix

This test never reaches its assertion and fails with a runTest timeout. What's the fix?

@Test
fun showsLoadingThenData() = runTest {
    val vm = FeedViewModel(load = { emptyList() })
    val states = mutableListOf<FeedUiState>()
    vm.state.collect { states += it }
    vm.onRefresh()
    assertEquals(false, states.last().isRefreshing)
}
Options
Hint 1 of 1

Does a StateFlow ever complete?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A Collect in backgroundScope.launch { } or use Turbine's test { }

Correct. A StateFlow never completes, so collect must not run in the test body.

Why not the others?
  • B Use runBlocking instead of runTest. Still hangs.
  • C Add delay(1000) after collect. That line is never reached.
  • D Call first() twice. That gets one value each time, but doesn't test a sequence.

30-second answer

For a finite Flow, flow.toList() or first() is enough. For hot flows like StateFlow, collect in backgroundScope into a list, trigger actions, and assert on what was collected, or use the Turbine library: flow.test { assertEquals(a, awaitItem()); ... cancelAndIgnoreRemainingEvents() }. Remember StateFlow conflates, so fast intermediate values may be skipped.

Practise saying it · 30 s. No recording, just a timer

Explain how to test flows.

Say your answer out loud, then check which of these points you covered:

  • toList/first for finite flows
  • backgroundScope or Turbine for hot flows
  • StateFlow conflates
  • WhileSubscribed needs a collector

Deep dive

Turbine fails if an expected item doesn't arrive or unexpected ones remain.

With UnconfinedTestDispatcher for the collector, every StateFlow update is seen as it happens.

For stateIn(WhileSubscribed), start a collector, or the upstream never runs.

Test the ViewModel's exposed state, not internal flows.

Common mistakes

  • Calling collect on a StateFlow inside runTest without a separate scope, so the test never finishes and times out.
  • Expecting to see every intermediate StateFlow value.
  • Forgetting that stateIn with WhileSubscribed needs a subscriber.

Senior take & follow-ups

Show Turbine, then explain conflation and WhileSubscribed traps.

Follow-up questions

Think of your answer first, then open the hint.

  1. Why might a stateIn flow never update in a test?

    Show hint

    No collector, so WhileSubscribed never starts the upstream.

  2. What does awaitItem do?

    Show hint

    Suspends until the next emission, failing on timeout.

Similar on iOS: Testing Combine publishers Combine tests collect values with expectations; Turbine is the Flow equivalent.

Suggest a fix if something here is wrong or out of date.