【问题标题】:Kotlin Coroutine Testing with Dispatchers.IO使用 Dispatchers.IO 进行 Kotlin 协程测试
【发布时间】:2020-04-24 11:08:45
【问题描述】:

所以也许有一个教程来解决这个问题,但我读过的那些都没有为我解决这个问题。我的结构如下,并且正在尝试进行单元测试,但是当我去测试时,我总是无法说明 repo 方法 doSomthing() 从未被调用过。我最好的猜测是因为我在不同的上下文中启动了一个新的协程。那我该如何测试呢?

存储库

interface Repository {
    suspend fun doSomething(): String
}

查看模型

class ViewModel(val repo: Repository) {
    val liveData = MutableLiveData<String>()
    fun doSomething {
    //Do something here
        viewModelScope.launch(Dispatchers.IO) {
            val data = repo.doSomething()
            withContext(Dispatchers.Main) {
                liveData.value = data
            }
        }
    }
}

查看模型测试

class ViewModelTest {
    lateinit var viewModel: ViewModel
    lateinit var repo: Repository

    @Before
    fun setup() {
        Dispatchers.setMain(TestCoroutineDispatcher())
        repo = mock<Repository>()
        viewModel = ViewModel(repo)
    }

    @Test
    fun doSomething() = runBlockingTest {
        viewModel.doSomething()
        viewModel.liveData.test().awaitValue().assertValue {
            // assert something
        }
        verify(repo).doSomthing()
    }
}

【问题讨论】:

  • viewModelScope 是如何设置的?如果你把它暴露给测试,你可以写viewModelScope.coroutineContext[Job]!!.children.forEach { it.join() }。但是,这仍然是不稳定的,因为测试不知道您将启动多少子作业以及何时启动它们。您可能会得到一个空的 children 列表。我处理类似问题的方法是使用assertTrueEventually 原语,该原语不断重试断言,直到满足或超时为止。
  • 另外,我对您的代码有一个一般性评论:在 Main 调度程序中启动协程并让 suspend fun doSomething() 根据需要在内部使用 IO 调度程序会更好。这不应该是调用者关心的问题。
  • viewModelSCope 是 Android 提供的一个扩展函数,它使用 Dispatchers.Main ... @MarkoTopolnik

标签: android unit-testing kotlin kotlin-coroutines


【解决方案1】:

根据谷歌:

Dispatchers 应该注入到您的ViewModels 中,以便您可以正确测试。您通过Dispatchers.setMainTestCorotutineDispatcher 设置为主调度程序,它控制MainDispatcher,但您仍然无法控制通过viewModelScope.launch(Dispatchers.IO) 启动的协程的执行。

通过构造函数传递Dispatcher 将确保您的测试和生产代码使用相同的调度程序。

@Rule 通常定义为:

  1. 通过 Dispatchers.setMain 覆盖 MainDispatcher(就像你正在做的那样)
  2. 使用 TestCoroutineDispatcher's 自己的 runBlockingTest() 来实际运行测试。

Here 是关于去年 Android 开发者峰会上发生的测试和协程的精彩演讲。

here 就是这种@Rule 的一个例子。 (无耻的插件。在那个 repo 上也有协程测试的例子)

【讨论】:

  • 太好了!也可以通过重载 viewModel 构造函数来使用默认 Dispatcher - 在 Kotlin class MyViewModel( private val dispatcher : CoroutineDispatcher = Dispatchers.IO) 这样我只需要在测试时设置调度程序。
猜你喜欢
  • 2018-05-13
  • 1970-01-01
  • 2018-04-20
  • 2018-06-13
  • 2019-06-06
  • 1970-01-01
  • 2022-01-11
  • 2019-08-23
  • 1970-01-01
相关资源
最近更新 更多