android - Kotlin 协程 : Switching context when testing an Android Presenter

标签 android unit-testing kotlin kotlinx.coroutines

我最近开始在我的 Android 项目中使用 kotlin 协程,但我在使用它时遇到了一些问题。许多人会称之为代码味道。

我使用的是 MVP 架构,其中协程在我的演示器中像这样启动:

// WorklistPresenter.kt
...
override fun loadWorklist() {
    ...
    launchAsync { mViewModel.getWorklist() }
    ...

launchAsync 函数是这样实现的(在我的 WorklistPresenter 类扩展的 BasePresenter 类中):

@Synchronized
protected fun launchAsync(block: suspend CoroutineScope.() -> Unit): Job {
    return launch(UI) { block() }
}

问题在于我使用的是依赖于 Android 框架的 UI 协程上下文。如果不遇到 ViewRootImpl$CalledFromWrongThreadException,我无法将其更改为另一个协程上下文。为了能够对此进行单元测试,我使用 launchAsync 的不同实现创建了我的 BasePresenter 的副本:

protected fun launchAsync(block: suspend CoroutineScope.() -> Unit): Job {
    runBlocking { block() }
    return mock<Job>()
}

对我来说这是个问题,因为现在我的 BasePresenter 必须在两个地方维护。所以我的问题是。如何更改我的实现以支持轻松测试?

最佳答案

我最近了解了 Kotlin 协程,教我的人向我展示了解决这个问题的好方法。

您创建一个提供上下文的接口(interface),具有默认实现:

interface CoroutineContextProvider {
    val main: CoroutineContext
        get() = Dispatchers.Main
    val io: CoroutineContext
        get() = Dispatchers.IO

    class Default : CoroutineContextProvider
}

然后您将此 (CoroutineContextProvider.Default()) 手动或使用注入(inject)框架注入(inject)到您的 Presenter 构造函数中。然后在您的代码中使用它提供的上下文:provider.mainprovider.io;或者你想定义的任何东西。现在您可以愉快地使用 launchwithContext 使用来自您的提供者对象的这些上下文,知道它会在您的应用程序中正常工作,但您可以在测试期间提供不同的上下文。

从您的测试中注入(inject)此提供程序的不同实现,其中所有上下文都是 Dispatchers.Unconfined

class TestingCoroutineContextProvider : CoroutineContextProvider {
    @ExperimentalCoroutinesApi
    override val main: CoroutineContext
        get() = Dispatchers.Unconfined
    @ExperimentalCoroutinesApi
    override val io: CoroutineContext
        get() = Dispatchers.Unconfined
}

当您模拟挂起函数时,用 runBlocking 包裹调用它,这将确保所有操作都发生在调用线程(您的测试)中。解释了here (请参阅有关“不受限与受限 Dispatcher”的部分)。

关于android - Kotlin 协程 : Switching context when testing an Android Presenter,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48205095/

相关文章:

android - 为什么 google play protect 将我的应用程序标记为有害?

java - 导入和别名练习

java - 如何测试注入(inject)了 Koin 的 viewModel?

Java:如何检查Key是否映射到HashMap中?

java - 在 Android 中打乱数组

c# - "Cannot verify on real object - use a fake object instead"异常

reactjs - 为什么我必须自己调用 Promise.then() 来测试 ES6 Promise?

unit-testing - 您是否将单元测试与集成测试分开?

java - 如何在android中使用另一个应用程序打开文件

android - 如何创建像 Google Docs App [Img inside] 这样的介绍 slider ?