https://kotlinlang.org logo
#coroutines
Title
# coroutines
g

George Theocharis

08/04/2020, 10:48 AM
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

Giorgos Neokleous

08/04/2020, 10:52 AM
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

George Theocharis

08/04/2020, 10:53 AM
It just emits the last value and not the intermediate ones. Thus it’s a bit different than observeForever.
w

wasyl

08/04/2020, 10:58 AM
Do you start observing immediately? LiveData also emits only the latest value upon subscription (and then updates),
StateFlow
should work the same way
g

George Theocharis

08/04/2020, 11:01 AM
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

Giorgos Neokleous

08/04/2020, 11:13 AM
What if you add
onEach { print(it) }
can you see all the updates? 🤔
g

George Theocharis

08/04/2020, 11:17 AM
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

streetsofboston

08/04/2020, 12:01 PM
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

George Theocharis

08/04/2020, 12:50 PM
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

wasyl

08/04/2020, 12:58 PM
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

streetsofboston

08/04/2020, 1:05 PM
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

Giorgos Neokleous

08/04/2020, 1:13 PM
@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

streetsofboston

08/04/2020, 1:36 PM
Yup, using the TestCoroutineDispatcher in TestCoroutineScope should work as well.
g

George Theocharis

08/04/2020, 2:45 PM
Oh thanks! Needed to advance until idle!
👍 1
8 Views