如何为使用activityViewModels的Fragment编写Instrumentation测试?Mockito无法Mock final类ViewModel的问题及是否需用Mockk的疑问
如何为使用activityViewModels的Fragment编写Instrumentation测试?Mockito无法Mock final类ViewModel的问题及是否需用Mockk的疑问
嗨,太懂你这个头疼的问题了!我之前在测试依赖Activity共享ViewModel的Fragment时,也踩过几乎一模一样的坑,咱们一步步拆解来解决~
先解决Mockito无法Mock final类的问题
你提到试过mock-maker-inline但没生效,大概率是配置位置不对!因为**Instrumentation测试(也就是androidTest目录下的测试)**和普通单元测试的配置路径不一样:
- 正确的做法是在
src/androidTest/resources下创建mockito-extensions文件夹,然后在里面新建org.mockito.plugins.MockMaker文件,文件内容只有一行:mock-maker-inline,注意不要加任何多余的空格、换行或者注释。很多人会不小心把这个文件放到src/test/resources里,导致Instrumentation测试根本读不到配置,自然没法Mock final类。
如果确认配置位置没错还是不行,可以试试升级Mockito的版本,有些旧版本对Kotlin final类的Mock支持有bug,升级到最新稳定版可能会解决。
测试使用activityViewModels的Fragment的正确姿势
当Fragment通过activityViewModels()获取Activity级的ViewModel时,我们需要让测试中的Activity提供我们Mock好的ViewModel实例,具体步骤如下:
- 先搞定ViewModel的Mock(不管用Mockito还是后面说的Mockk)
- 不用Mock
ViewModelProvider.Factory,写一个简单的自定义工厂更靠谱,避免Mock工厂带来的额外问题:// 假设已经Mock好了SomeViewModel val mockViewModel = mock(SomeViewModel::class.java) val testViewModelFactory = object : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return when (modelClass) { SomeViewModel::class.java -> mockViewModel as T else -> throw IllegalArgumentException("Unknown ViewModel type: ${modelClass.name}") } } } - 让Fragment关联的Activity使用这个工厂:
用FragmentScenario启动Fragment后,在onFragment回调里获取Activity的ViewModelStoreOwner,手动用我们的工厂初始化ViewModel,这样Fragment调用activityViewModels()时就会拿到Mock实例:
这样后续Fragment里调用val fragmentScenario = launchFragmentInContainer<YourTargetFragment>() fragmentScenario.onFragment { fragment -> val activityViewModelStore = fragment.requireActivity() // 提前用我们的工厂初始化Activity级的ViewModel ViewModelProvider(activityViewModelStore, testViewModelFactory)[SomeViewModel::class.java] }activityViewModels<SomeViewModel>()就会拿到我们的Mock实例了。
到底要不要用Mockk?
如果Mockito的final类Mock问题实在搞不定,或者你想更顺畅地测试Kotlin代码,Mockk绝对是更优选择:
- Mockk默认支持Mock final类、object类、静态方法等,不需要额外配置
mock-maker-inline这类文件,语法更贴合Kotlin。比如Mock一个final的ViewModel只需要一行:val mockViewModel = mockk<SomeViewModel>() - 它对Kotlin的特性支持更友好,比如协程、扩展函数等,后续测试复杂场景也会更省心。
当然,如果你的项目已经大量使用Mockito,且能搞定mock-maker-inline的配置,继续用Mockito也没问题。但如果配置后还是踩坑,不如直接换Mockk,学习成本不高,能省不少麻烦。
备注:内容来源于stack exchange,提问作者rminaj
相关产品推荐
相关产品推荐

