首先,您需要以某种方式使您的协程作用域可注入,可以通过手动为其创建提供程序,也可以使用像 dagger 这样的注入框架。这样,当您测试 ViewModel 时,您可以使用测试版本覆盖协程范围。
有几种选择可以做到这一点,您可以简单地使 ViewModel 本身可注入(有关该内容的文章在这里:https://medium.com/chili-labs/android-viewmodel-injection-with-dagger-f0061d3402ff https://medium.com/chili-labs/android-viewmodel-injection-with-dagger-f0061d3402ff)
或者,您可以手动创建 ViewModel 提供程序并在创建的位置使用它。无论如何,我强烈建议使用某种形式的依赖注入以实现真正的可测试性。
无论如何,你的 ViewModel 需要有它的 CoroutineScopeprovided,不实例化协程作用域本身。
换句话说,你可能想要
class MovieListViewModel(val couroutineScope: YourCoroutineScope) : ViewModel() {}
or maybe
class MovieListViewModel @Inject constructor(val coroutineScope: YourCoroutineScope) : ViewModel() {}
无论您如何进行注入,下一步都是创建您自己的 CoroutineScope 接口,您可以在测试上下文中覆盖该接口。例如:
interface YourCoroutineScope : CoroutineScope {
fun launch(block: suspend CoroutineScope.() -> Unit): Job
}
这样,当您为应用程序使用作用域时,您可以使用一个作用域,例如生命周期协程作用域:
class LifecycleManagedCoroutineScope(
private val lifecycleCoroutineScope: LifecycleCoroutineScope,
override val coroutineContext: CoroutineContext = lifecycleCoroutineScope.coroutineContext) : YourCoroutineScope {
override fun launch(block: suspend CoroutineScope.() -> Unit): Job = lifecycleCoroutineScope.launchWhenStarted(block)
}
对于您的测试,您可以使用测试范围:
class TestScope(override val coroutineContext: CoroutineContext) : YourCoroutineScope {
val scope = TestCoroutineScope(coroutineContext)
override fun launch(block: suspend CoroutineScope.() -> Unit): Job {
return scope.launch {
block.invoke(this)
}
}
}
现在,由于您的 ViewModel 使用 YourCoroutineScope 类型的作用域,并且在上面的示例中,生命周期和测试版本都实现了 YourCoroutineScope 接口,因此您可以在不同情况下使用不同版本的作用域,即应用程序与测试。