[英]Testing Android ViewModelProvider with a mock ViewModel
我很高兴使用新的 Android 架构组件 ViewModel 系统,它很好地将 Activity/Fragment/Layout 渲染问题与 ViewModel 逻辑分开。 我已经成功地对 ViewModel 进行了单独的单元测试,现在想通过为各种 state 场景的 Activity/Fragment 提供模拟的 ViewModel 来尝试一些屏幕截图测试。
我已经成功配置了我的 androidTests 以便能够在我的设备测试中使用 Mockito,这部分效果很好。
但是, 官方推荐的调用 ViewModelProvider 或by viewModels<>
委托的方式似乎并没有提供注入模拟 ViewModel 的方法。 我宁愿不添加一个完整的 DI 框架只是为了解决文档中的这个遗漏,所以我想知道是否有人有任何成功的例子来提供带有官方 Android 架构组件的模拟 ViewModel,而没有 Dagger 或 Hilt 的额外依赖。
1 年前唯一相关的答案建议使用ActivityTestRule
并手动控制活动生命周期,但该规则已被弃用,取而代之的是不提供此控制的activityScenarioRule
。
您可以使用ViewModelProvider
,因此您可以将测试中的ViewModelProvider.Factory
替换为模拟。 例如通过使用:
viewModel = ViewModelProvider(this, ViewModelFactoryOfFactory.INSTANCE)
.get(MyViewModel::class.java)
在哪里:
object ViewModelFactoryOfFactory {
// The default factory.
var INSTANCE: ViewModelProvider.Factory = MyViewModelFactory()
private set
// To set the factory during tests.
@VisibleForTesting
fun setTestFactory(factory: ViewModelProvider.Factory) {
ViewModelFactoryOfFactory.INSTANCE = factory
}
}
然后在测试设置中可以:
ViewModelFactoryOfFactory.setTestFactory(mockFactory)
有人可能会争辩说,所有这一切都可以被工厂取代以获得 ViewModel。
另一种选择可能只是使ViewModelProvider.Factory
成为 Activity 或片段中的字段/属性,因此它也可以从测试中设置,也允许更好的 memory 管理。
我决定重写by viewModels
委托以检查模拟 ViewModels 的 map 中的实例,因此如果找不到 ViewModel,我的活动可以使用普通委托模式并提供自己的工厂。
val mockedViewModels = HashMap<Class<*>, ViewModel>()
@MainThread
inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
): Lazy<VM> {
// the production producer
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
return createMockedViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
/// ... and similar for the fragment-ktx delegates
/**
* Wraps the default factoryPromise with one that looks in the mockedViewModels map
*/
fun <VM : ViewModel> createMockedViewModelLazy(
viewModelClass: KClass<VM>,
storeProducer: () -> ViewModelStore,
factoryPromise: () -> ViewModelProvider.Factory
): Lazy<VM> {
// the mock producer
val mockedFactoryPromise: () -> ViewModelProvider.Factory = {
// if there are any mocked ViewModels, return a Factory that fetches them
if (mockedViewModels.isNotEmpty()) {
object: ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return mockedViewModels[modelClass] as T
?: factoryPromise().create(modelClass) // return the normal one if no mock found
}
}
} else {
// if no mocks, call the normal factoryPromise directly
factoryPromise()
}
}
return ViewModelLazy(viewModelClass, storeProducer, mockedFactoryPromise)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.