使用模拟ViewModel测试Android ViewModelProvider
我很高兴能使用新的Android架构组件ViewModel系统,它很好地将活动/片段/布局渲染问题与ViewModel逻辑分离开来。我已经成功地对ViewModel进行了独立的单元测试,现在我想通过为各种状态场景的活动/片段提供模拟ViewModels来尝试一些屏幕截图测试 我已经成功地将我的androidTests配置为能够在我的设备测试中使用Mockito,这一部分非常有效 但是,调用ViewModelProvider或通过viewModels委派使用模拟ViewModel测试Android ViewModelProvider,android,unit-testing,mvvm,Android,Unit Testing,Mvvm,我很高兴能使用新的Android架构组件ViewModel系统,它很好地将活动/片段/布局渲染问题与ViewModel逻辑分离开来。我已经成功地对ViewModel进行了独立的单元测试,现在我想通过为各种状态场景的活动/片段提供模拟ViewModels来尝试一些屏幕截图测试 我已经成功地将我的androidTests配置为能够在我的设备测试中使用Mockito,这一部分非常有效 但是,调用ViewModelProvider或通过viewModels委派的方法似乎并没有提供注入模拟viewMode
的方法似乎并没有提供注入模拟viewModels的方法。我不想仅仅为了解决文档中的这一遗漏而添加一个完整的DI框架,所以我想知道是否有人能够成功地为mocked ViewModels提供官方Android架构组件,而不需要额外的Dagger或Hilt依赖项
从1年前开始,建议使用ActivityTestRule
并手动控制活动生命周期,但该规则已被弃用,取而代之的是不提供此控制的activityScenarioRule
。您可以使用ViewModelProvider
,因此,您可以使用模拟替换测试中的ViewModelProvider.Factory
。例如,通过使用:
viewModel=ViewModelProvider(这是ViewModelFactoryOfFactory.INSTANCE)
.get(MyViewModel::class.java)
其中:
对象视图模型工厂工厂{
//默认工厂。
变量实例:ViewModelProvider.Factory=MyViewModelFactory()
专用设备
//在测试期间设置出厂设置。
@可视性测试
fun setTestFactory(工厂:ViewModelProvider.factory){
ViewModelFactoryOffFactory.INSTANCE=工厂
}
}
然后在测试设置中,可以:
ViewModelFactoryOfFactory.setTestFactory(mockFactory)
有人可能会说,所有这些都可以由工厂来获得ViewModel来代替
另一个选项可以是将ViewModelProvider.Factory
设置为活动或片段中的字段/属性,这样也可以通过测试进行设置,从而实现更好的内存管理。我决定重写by viewModels
委托,以检查模拟viewModels映射中的实例,因此,如果找不到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)
}
val mockedViewModels=HashMap()
@主线
inline fun ComponentActivity.viewModels(
noinline factoryProducer:(()->ViewModelProvider.Factory)?=null
):懒惰{
//生产商
val factoryPromise=factoryProducer?:{
defaultViewModelProviderFactory
}
返回createMockedViewModelLazy(VM::class,{viewModelStore},factoryPromise)
}
/// ... 对于片段ktx委托也类似
/**
*将默认factoryPromise包装为在mockedViewModels映射中查看的factoryPromise
*/
有趣的createMockedViewModelLazy(
viewModelClass:KClass,
storeProducer:()->ViewModelStore,
factoryPromise:()->ViewModelProvider.Factory
):懒惰{
//模拟制作人
val mockedFactoryPromise:()->ViewModelProvider.Factory={
//如果存在任何模拟的ViewModels,请返回获取它们的工厂
if(mockedViewModels.isNotEmpty()){
对象:ViewModelProvider.Factory{
重写趣味创建(modelClass:Class):T{
将mockedViewModels[modelClass]返回为T
?:factoryPromise().create(modelClass)//如果未找到模拟,则返回正常值
}
}
}否则{
//如果没有模拟,请直接致电正常的factoryPromise
工厂承诺
}
}
返回ViewModelLazy(viewModelClass、storeProducer、mockedFactoryPromise)
}