低SDK版本下如何Mock注入ViewModel中的userObserver(LiveData<UserResponse>)
问题描述
class CustomActivity : BaseActivity() { @Inject lateinit var viewModelFactory: ViewModelProviderFactory private val mViewModel: SplashViewModel by viewModels { viewModelFactory } override fun onResume() { super.onResume() observeOrderResponse() } private fun observeOrderResponse() { mViewModel.userObserver.observe(this) { userResponse -> updateUIBasedOnUserResponse(userResponse) } }
在上述代码中,需要使用指定的UserResponse来Mock userObserver(LiveData<UserResponse>),但应用最低SDK版本为26,无法使用AppComponentFactory,该如何Mock这个依赖注入的ViewModel中的观察者?
解决方案
方法一:直接Mock ViewModel并替换实例
借助MockK或Mockito这类Mock框架,创建预设了userObserver的ViewModel实例,再通过反射替换Activity中的mViewModel:
// 测试示例(使用MockK) @Test fun testUserResponseHandling() { // 1. 构造测试用的UserResponse val testUserResponse = UserResponse(/* 填充测试所需字段 */) // 2. 创建包含测试数据的LiveData val testLiveData = MutableLiveData<UserResponse>().apply { value = testUserResponse } // 3. Mock SplashViewModel,指定userObserver返回测试LiveData val mockViewModel = mockk<SplashViewModel>() every { mockViewModel.userObserver } returns testLiveData // 4. 启动Activity并替换ViewModel实例 val activity = launchActivity<CustomActivity>().get() val viewModelField = CustomActivity::class.java.getDeclaredField("mViewModel") viewModelField.isAccessible = true viewModelField.set(activity, mockViewModel) // 5. 触发观察逻辑,验证UI更新 activity.onResume() // 此处添加验证updateUIBasedOnUserResponse执行结果的逻辑,比如检查UI控件状态 }
方法二:使用测试专用的ViewModelFactory
为测试场景实现自定义的ViewModelProviderFactory,直接返回预设好userObserver的ViewModel,再替换Activity中的工厂实例:
// 测试用Factory实现 class TestSplashViewModelFactory(private val targetViewModel: SplashViewModel) : ViewModelProviderFactory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return targetViewModel as T } } // 测试方法 @Test fun testWithCustomFactory() { val testUserResponse = UserResponse(/* 测试数据 */) val testLiveData = MutableLiveData<UserResponse>().apply { value = testUserResponse } // 创建ViewModel实例并修改其userObserver val testViewModel = SplashViewModel() val observerField = SplashViewModel::class.java.getDeclaredField("userObserver") observerField.isAccessible = true observerField.set(testViewModel, testLiveData) // 初始化测试用Factory并注入Activity val testFactory = TestSplashViewModelFactory(testViewModel) val activity = launchActivity<CustomActivity> { val factoryField = CustomActivity::class.java.getDeclaredField("viewModelFactory") factoryField.isAccessible = true factoryField.set(this, testFactory) }.get() activity.onResume() // 验证UI更新逻辑 }
方法三:结合LiveData测试工具同步执行
使用InstantTaskExecutorRule让LiveData在测试中同步执行,避免异步回调的时序问题,搭配Mock框架控制数据输出:
// 添加测试规则,确保LiveData同步执行 @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() @Test fun testLiveDataObserverSync() { val testUserResponse = UserResponse(/* 测试数据 */) val testLiveData = MutableLiveData<UserResponse>() val mockViewModel = mockk<SplashViewModel>() every { mockViewModel.userObserver } returns testLiveData val activity = launchActivity<CustomActivity>().get() // 替换ViewModel实例 val viewModelField = CustomActivity::class.java.getDeclaredField("mViewModel") viewModelField.isAccessible = true viewModelField.set(activity, mockViewModel) // 触发观察后发送测试数据 activity.observeOrderResponse() testLiveData.postValue(testUserResponse) // 验证UI是否正确响应数据 }
注意事项
- 反射操作需要确保目标字段(
mViewModel、viewModelFactory或ViewModel中的userObserver)的可见性,通过setAccessible(true)开启访问权限 - 若
userObserver是private修饰的可变LiveData,需通过反射修改其值;如果是只读的,直接Mock返回预设LiveData即可 InstantTaskExecutorRule属于androidx.arch.core:core-testing库,需确保测试依赖中已引入
内容的提问来源于stack exchange,提问作者Maradiya Krupa
相关产品推荐
相关产品推荐

