首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >基于viewModelScope的Kotlin协同测试流程采集

基于viewModelScope的Kotlin协同测试流程采集
EN

Stack Overflow用户
提问于 2019-09-26 10:48:49
回答 2查看 6.5K关注 0票数 10

我想测试一个收集流的ViewModel方法。在收集器内部,一个LiveData对象会发生变异,我想在最后检查它。这大致是设置的样子:

代码语言:javascript
运行
复制
//Outside viewmodel
val f = flow { emit("Test") }.flowOn(Dispatchers.IO)

//Inside viewmodel
val liveData = MutableLiveData<String>()

fun action() {
    viewModelScope.launch { privateAction() }
}

suspend fun privateAction() {
    f.collect {
        liveData.value = it
    }
}

现在,当我在单元测试中调用action()方法时,测试在收集流之前就完成了。这就是测试的样子:

代码语言:javascript
运行
复制
@Test
fun example() = runBlockingTest {
    viewModel.action()
    assertEquals(viewModel.liveData.value, "Test")
}

我通过这个TestCoroutineDispatcher扩展使用Junit5,同时也使用LiveData的即时执行器扩展:

代码语言:javascript
运行
复制
    class TestCoroutineDispatcherExtension : BeforeEachCallback, AfterEachCallback, ParameterResolver {
    @SuppressLint("NewApi") // Only used in unit tests
    override fun supportsParameter(parameterContext: ParameterContext?, extensionContext: ExtensionContext?): Boolean {
        return parameterContext?.parameter?.type === testDispatcher.javaClass
    }

    override fun resolveParameter(parameterContext: ParameterContext?, extensionContext: ExtensionContext?): Any {
        return testDispatcher
    }

    private val testDispatcher = TestCoroutineDispatcher()

    override fun beforeEach(context: ExtensionContext?) {
        Dispatchers.setMain(testDispatcher)
    }

    override fun afterEach(context: ExtensionContext?) {
        Dispatchers.resetMain()
        testDispatcher.cleanupTestCoroutines()
    }
}

    class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback {
    override fun beforeEach(context: ExtensionContext?) {
        ArchTaskExecutor.getInstance()
            .setDelegate(object : TaskExecutor() {
                override fun executeOnDiskIO(runnable: Runnable) = runnable.run()
                override fun postToMainThread(runnable: Runnable) = runnable.run()
                override fun isMainThread(): Boolean = true
            })
    }

    override fun afterEach(context: ExtensionContext?) {
        ArchTaskExecutor.getInstance().setDelegate(null)
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-08-18 06:31:48

因此,我最后所做的只是将Dispatcher传递给viewmodel构造函数:

代码语言:javascript
运行
复制
class MyViewModel(..., private val dispatcher = Dispatchers.Main)

然后像这样使用它:

代码语言:javascript
运行
复制
viewModelScope.launch(dispatcher) {}

现在,当我用一个ViewModel实例化测试中的TestCoroutineDispatcher时,我可以重写它,然后提前时间,使用testCoroutineDispatcher.runBlockingTest {}等等。

票数 0
EN

Stack Overflow用户

发布于 2019-09-26 12:55:35

你也可以试试,

代码语言:javascript
运行
复制
fun action() = viewModelScope.launch { privateAction() }

suspend fun privateAction() {
    f.collect {
        liveData.value = it
    }
}

@Test
fun example() = runBlockingTest {
    viewModel.action().join()
    assertEquals(viewModel.liveData.value, "Test")
}

代码语言:javascript
运行
复制
fun action() {
    viewModelScope.launch { privateAction()
}

suspend fun privateAction() {
    f.collect {
        liveData.value = it
    }
}

@Test
fun example() = runBlockingTest {
    viewModel.action()
    viewModel.viewModelScope.coroutineContext[Job]!!.join()
    assertEquals(viewModel.liveData.value, "Test")
}

你也可以试试这个,

代码语言:javascript
运行
复制
suspend fun <T> LiveData<T>.awaitValue(): T? {
    return suspendCoroutine { cont ->
        val observer = object : Observer<T> {
            override fun onChanged(t: T?) {
                removeObserver(this)
                cont.resume(t)
            }
        }
        observeForever(observer)
    }
}

@Test
fun example() = runBlockingTest {
    viewModel.action()
    assertEquals(viewModel.liveData.awaitValue(), "Test")
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58115153

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档