Android开发中如何Mock Hilt注入的ViewModel(Assisted Factory为final无法匿名继承)
Hilt 注入 Assisted ViewModel 的 Mock 方案
问题根因
编译报错本质是测试思路的偏差:
- Hilt 为带 Assisted 注入的 ViewModel 生成的
MyViewModel_AssistedFactory是编译期自动生成的final类,设计上就不允许被继承扩展 - 试图匿名实现这个final类的操作完全没有必要:生产模块里
@Binds绑定的实际暴露类型是ViewModelAssistedFactory<out ViewModel>,那个生成的工厂只是这个接口的生产侧实现而已,测试阶段完全可以绕开这个实现类,直接替换接口的提供逻辑,从根源上避开final类继承的限制。
具体实现步骤
- 第一步:定义测试专属的替代模块
不需要手动写匿名类继承生成的工厂,直接在测试模块里提供自定义的ViewModelAssistedFactory实现,把你Mock的ViewModel实例包装进去即可。如果是Hilt 2.38以上版本,可以直接用@TestInstallIn注解定义测试模块,避免代码污染生产构建:@TestInstallIn( components = [ActivityRetainedComponent::class], replaces = ActivityModule::class ) @Module object TestActivityModule { @Provides @IntoMap @StringKey("my.package.MyViewModel") fun provideMockMyViewModelFactory(): ViewModelAssistedFactory<out ViewModel> { // 直接实现工厂接口,返回Mock的ViewModel实例 return ViewModelAssistedFactory { mock<MyViewModel>() } } } - 第二步:调整测试类代码
把之前试图构造MyViewModel_AssistedFactory匿名实现的报错代码全部删除,Hilt会自动把测试模块提供的Mock工厂注入到对应的ViewModel Map中,Fragment运行时拿到的就是Mock出来的ViewModel实例:@HiltAndroidTest class FragmentTest { @get:Rule val hiltRule = HiltAndroidRule(this) // 可选:直接注入ViewModel工厂Map,方便手动控制Mock的行为 @Inject lateinit var viewModelFactoryMap: Map<String, @JvmSuppressWildcards ViewModelAssistedFactory<out ViewModel>> @Before fun setup() { hiltRule.inject() } @Test fun testFragment正常渲染() { // 取出对应key的工厂,拿到Mock的ViewModel实例 val mockViewModel = viewModelFactoryMap["my.package.MyViewModel"]?.create() as MyViewModel // 用Mockito配置Mock的返回值、校验交互逻辑 `when`(mockViewModel.pageTitle).thenReturn("测试标题") // 后续启动Fragment,验证UI表现即可 } }
兼容与注意事项
- 如果使用的Hilt版本还不支持
@TestInstallIn注解,可以把上述的TestActivityModule移动到androidTest源集下,使用普通的@InstallIn(ActivityRetainedComponent::class)注解,配合测试类上原有的@UninstallModules(ActivityModule::class)注解即可达到相同效果 - 完全不需要为了Mock这个场景额外开启Mockito的final类Mock支持,那个生成的工厂类在测试流程中根本不会被加载
- 替换绑定的时候一定要保证
@StringKey的字符串和生产模块里的完全一致,否则会出现绑定失效,还是拿到真实的ViewModel实例 - 如果你的ViewModel构造里有Assisted参数,直接在工厂的
create()方法里返回对应参数的Mock对象即可,不需要走真实的参数解析流程
内容的提问来源于stack exchange,提问作者DeveloperKurt
相关产品推荐
相关产品推荐

