https://kotlinlang.org logo
Title
m

Mini

02/08/2022, 7:11 PM
Im struggling to understand why .map and .combine works differently with .stateIn in my tests The test testing .map works as expected, the one testing the combine does not.
class MyTest() : FunSpec({

    val dispatcher = TestCoroutineDispatcher()
    listeners(MainCoroutineListener(dispatcher), MtLogListener())

    class MyViewModel : ViewModel(){

        val x = MutableStateFlow(5)
        val y = MutableStateFlow(2)

        val doubleX = x.map {
            it * 2
        }.stateIn(
            viewModelScope,
            SharingStarted.Eagerly,
            null
        )

        val combinedFlow = x.combine(y){ xValue, yValue ->
            xValue * yValue
        }.stateIn(
            viewModelScope,
            SharingStarted.Eagerly,
            null
        )
    }

    val viewModel = MyViewModel()

    test("stateflow mapped"){
        viewModel.doubleX.value shouldBe 10
        viewModel.x.value = 9
        viewModel.doubleX.value shouldBe 18
    }

    test("combined"){
        viewModel.x.value = 10
        viewModel.y.value = 3
        viewModel.combinedFlow.value shouldBe 30
    }
}){
    override fun isolationMode(): IsolationMode = IsolationMode.InstancePerLeaf
}
n

Nick Allen

02/08/2022, 10:02 PM
TestCoroutineDispatcher
will immediately run top-level launched“ coroutines, but not nested coroutines (this is to avoid stack overflows).
combine
launches coroutines to collect from each
Flow
.
map
just calls the transform directly. Here the top-level coroutine is the
stateIn
coroutine collecting from the
combine/map
derived
Flow
. For
map
, that's all there is, no nested launching so it runs completely. For
combine
the nested coroutines to read from the combined `Flow`s are not run immediately. If you tell the dispatcher to runCurrent before checking the value, then it'll work. I'm assuming the Main dispatcher is setup correctly (it's not obvious to me from the snippet).
❤️ 1
🙌 1