Hello :wave: Is it correct that state flow will ...
# coroutines
g
Hello 👋 Is it correct that state flow will only emit the latest value? Seems I cannot collect every different state and assert the order, except the latest. With live data though you can with observe forever. Is something i can do to test that order of states?
g
You can
collect
the same way as a
Flow
. If you are actively collecting/observing it should emit all the values. The
value
will always be the current value.
g
It just emits the last value and not the intermediate ones. Thus it’s a bit different than observeForever.
w
Do you start observing immediately? LiveData also emits only the latest value upon subscription (and then updates),
StateFlow
should work the same way
g
I am doing an mviish approach where i start collecting a state (this is my state flow) and then emit an event to start some usecases. This results in multiple updates in the stateflow but it drops the intermediate states and collects only the last as it finishes immediately in unit tests.
👆 1
g
What if you add
onEach { print(it) }
can you see all the updates? 🤔
g
Nope, i think it’s by design to only see the latest. I am just wondering if there is a way to see them.
s
Subscribe/collect the StateFlow as soon as possible, not at the end, at the last moment. The collector should see all emissions of the StateFlow over time. However, changing a StateFlow's current value is not a suspend function: If all emissions happen right after each other in the same thread, the collector may never get a chance to actually start collecting. To 'fix' this, add a bunch of delay or yield calls to give the collector some cpu cycles to actually collect.
g
But adding delays just to test it seems awful. What works apparently is to use asLiveData() to the private mutable state flow. Then the observer gets notified correctly.
w
It’s not really about adding delays in tests just for tests to work: if your model would emit synchronously without yielding, you might observe the same behavior as in tests (“dropped” events). Also afair
StateFlow
only emits if the state has changed so you’d also not get every value emitted
☝️ 1
s
Even LiveData will drop events if its
postValue
method is called, instead of its
value
setter. Try running your test (both emissions and collectors) on the Main Immediate dispatcher and call
yield
before collecting the next value. https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-coroutine-dispatcher/immediate.html
g
@George Theocharis
Copy code
val scope = TestCoroutineScope()

    @Test
    fun testStateFlow() = scope.runBlockingTest {
        val stateFlow = MutableStateFlow(0)
        val values = mutableListOf<Int>()

        val job = launch {
            stateFlow
                .onEach { values.add(it) }
                .collect()
        }
        
        launch {
            while (stateFlow.value != 5) {
                stateFlow.value = stateFlow.value + 1
            }
        }

        scope.advanceUntilIdle()

        Truth.assertThat(values).containsExactly(0, 1, 2, 3, 4, 5).inOrder()

        job.cancel()
        scope.cleanupTestCoroutines()
    }
This works for me fine 🤔 Isn't this what you are describing?
s
Yup, using the TestCoroutineDispatcher in TestCoroutineScope should work as well.
g
Oh thanks! Needed to advance until idle!
👍 1