由 StateIn 创建的 StateFlow 单元测试

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

我有一个视图模型类,它使用使用 StateIn 运算符创建的 StateFlow。

I.E.

private val _state = MutableStateFlow(MyState())
private val myItems = myRepository.myFlow.stateIn(
        viewModelScope, SharingStarted.WhileSubscribed(), emptyList<MyObject>())
val state: StateFlow<MyState> = combine(_state, myItems ) { state, myItems ->
        ..///
    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), MyState())

在阅读文档以及各种文章和SO问题(已过时)时,我发现您应该使用以下方法来测试它:

backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
        //Collect your flow
    }

我遇到的问题是,当我创建单元测试来测试我的视图模型时,状态的收集对于发生的断言来说不够快。

@Test
fun checkMyItemTest() = runTest {

        val results = mutableListOf<MyState>()
        val job = backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
            viewModel.state.collect {
                results.add(it)
            }
        }

        viewModel.onEvent(MyEvent.DoSomething())

        assert(results[0].myStateParameter) //Perform some check on results[0]
        assert(results[1].myStateParameter) //Perform some check on results[1]

        job.cancel()
    }

上面的测试在第二个断言中失败,因为 results 中只有一个元素。 我通过在两个断言调用之间插入 50 毫秒的

Thread.sleep
调用来使其工作,但这对我来说似乎不是最好的方法。

@Test
    fun checkMyItemTest() = runTest {
    
            val results = mutableListOf<MyState>()
            val job = backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
                viewModel.state.collect {
                    results.add(it)
                }
            }
    
            viewModel.onEvent(MyEvent.DoSomething())
    
            assert(results[0].myStateParameter) //Perform some check on results[0]
            Thread.sleep(50) /// <---- THIS
            assert(results[1].myStateParameter) //Perform some check on results[1]

            job.cancel()
        }

我目前对使用 Turbine 不感兴趣

所以我希望了解如何正确测试我的流程以及如何收集它发出的值。

android junit4 kotlin-stateflow kotlin-statein
2个回答
1
投票

我也是测试新手,但从我读过的内容来看,

runTest{}
跳过了延迟,与 runBlocking 没有太大区别,所以这可能有效:

@Test
fun checkMyItemTest() = runTest {

    val results = Channel<MyState>() // this is a rendevous channel
    val job = backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
        viewModel.state.collect {
            results.send(it)
        }
    }

    viewModel.onEvent(MyEvent.DoSomething())

    val result1 = channel.receive() //suspends till it has a corresponding send call
    val result2 = channel.receive()

    assert(result1.myStateParameter) //Perform some check on result1
    assert(result2.myStateParameter) //Perform some check on result2
}

我还没有运行代码。


0
投票

您应该使用 StandardTestDispatcher 代替 UnconfinedTestDispathcher

@Test
fun checkMyItemTest() = runTest {

        val results = mutableListOf<MyState>()
        val job = backgroundScope.launch(StandardTestDispatcher(testScheduler)) {
            viewModel.state.collect {
                results.add(it)
            }
        }

        viewModel.onEvent(MyEvent.DoSomething())
        advanceUntilIdle()

        assert(results[0].myStateParameter) //Perform some check on results[0]
        assert(results[1].myStateParameter) //Perform some check on results[1]

        job.cancel()
    }

来自文档

记住 UnconfinedTestDispatcher 会急切地启动新的协程, 但这并不意味着它会急切地运行它们以完成 出色地。如果新协程挂起,其他协程将恢复 正在执行。

在本例中是视图模型状态的集合。它将收集默认的 StateFlow 值并放入您的

results

然后,当您发出新值时,协程不会等待该值,而是立即断言

result[1]

© www.soinside.com 2019 - 2024. All rights reserved.