观察 ViewModel 初始化中的 StateFlow 发射

问题描述 投票:0回答:2

我有一个视图模型,它接受初始

ViewState
对象,并具有可公开访问的
state
变量,可以收集。

class MyViewModel<ViewState>(initialState: ViewState) : ViewModel() {
    val state: StateFlow<ViewState> = MutableStateFlow(initialState)
    val errorFlow: SharedFlow<String> = MutableSharedFlow()

    init {
        performNetworkCall()
    }

    private fun performNetworkCall() = viewModelScope.launch {
        Network.makeCall(
            "/someEndpoint",
            onSuccess = {
                (state as MutableStateFlow).tryEmit(<some new state>)
            },
            onError = {
                (errorFlow as MutableSharedFlow).tryEmit("network failure")
            }
        )
    }
}

从片段观察此状态时,我可以看到初始状态(例如正在加载),并且当网络调用成功完成时我会收集更改(例如,到已加载状态)

但是,我不知道如何从我的

ViewModelUnitTest
观察这种发射。 我使用 kotlin 涡轮机来测试我的状态和共享流的排放,但我只能观察在调用
viewModel.state.test
viewModel.errorFlow.test
之后发生的排放。

由于我无法在 ViewModel 初始化之前引用

viewModel.state
viewModel.errorFlow
,因此如何编写测试来验证我的初始化逻辑是否正确执行并根据
Network.makeCall
的模拟行为发出预期结果 - 无论是是
state
的新发射还是
errorFlow
发射?

android kotlin kotlin-coroutines android-viewmodel kotlin-flow
2个回答
1
投票

由于您的网络类在视图模型中没有任何引用,因此不可能模拟/伪造

Network
类的行为。因此,为
Network
创建一个参考。要捕获
init
块中发生的情况,您可以在测试类中延迟初始化视图模型。 here中描述了第二种方式。粗略地说,您的课程类似于:

class MyViewModel<ViewState>(
    initialState: ViewState,
    network: Network
) : ViewModel() {
    val state: StateFlow<ViewState> = MutableStateFlow(initialState)
    val errorFlow: SharedFlow<String> = MutableSharedFlow()

    init {
        performNetworkCall()
    }

    private fun performNetworkCall() = viewModelScope.launch {
        network.makeCall(
            "/someEndpoint",
            onSuccess = {
                (state as MutableStateFlow).tryEmit(<some new state>)
            },
            onError = {
                (errorFlow as MutableSharedFlow).tryEmit("network failure")
            }
        )
    }
}

@ExperimentalCoroutinesApi
class MyViewModelTest {

    @get:Rule
    var coroutinesTestRule = CoroutinesTestRule()

    val viewModel by lazy { MyViewModel(/*pass arguments*/) }
}

CoroutineTestRule
可以在这里找到。


0
投票

在测试中,您应该模拟网络并延迟其响应。还要确保 viewModel 使用 TestDispatcher:

class MyViewModelTest() {
    val dispatcher = TestCoroutineDispatcher()

    @Before
    fun setup() { // ensure that viewModel is using TestDispatcher
        Dispatchers.setMain(dispatcher)
    }

    @After
    fun tearDown() {
        Dispatchers.resetMain()
    }

    internal class WatchedListsViewModelTest {
        @Test
        fun `should close with error when fetch lists from repository fails`() = runTest {
            val initialState = // ...
            val network: Network = mockk {
                on { makeCall(any(), any(), any()) } doAnswer { delay(1) } // delay to give time for observer to subscribe
            }

            val viewModel = MyViewModel(initalState, network)

            val result = viewModel.state.take(1).toList()

            advanceUntilIdle()

            assertEquals("expected value", result.first())
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.