ViewModel单元测试如何正确传入CoroutineScope解决空指针异常
报错原因
你通过Mockito mock了CoroutineScope,但mock对象没有真实的协程上下文实现,调用launch方法时访问上下文属性就会触发空指针,协程测试场景不需要mock Scope,直接使用官方测试库提供的真实测试Scope即可。
解决方案
第一步:添加依赖
先引入对应测试库(版本请和你项目内的协程、Jetpack版本保持一致):
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3" testImplementation "androidx.arch.core:core-testing:2.2.0"
第二个依赖用于处理LiveData的同步执行逻辑。
第二步:修改测试代码
@RunWith(MockitoJUnitRunner::class) class MainActivityViewModelTest { // 让LiveData的所有任务同步执行,避免异步等待问题 @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() @Mock private lateinit var dataRepository: DataRepository @Mock private lateinit var dataObserver: Observer<Result<List<Character>>> // 直接使用官方提供的测试Scope,不要mock private val testScope = TestScope() // 手动初始化ViewModel,不要用@InjectMocks private lateinit var mainActivityViewModel: MainActivityViewModel @Before fun setUp() { mainActivityViewModel = MainActivityViewModel(testScope, dataRepository) mainActivityViewModel.charactersLiveData.observeForever(dataObserver) } @After fun tearDown() { mainActivityViewModel.charactersLiveData.removeObserver(dataObserver) // 测试结束后取消Scope,避免内存泄漏 testScope.cancel() } @Test fun fetchCharacters() = runTest { // 构造mock返回值 val mockCharacters = arrayListOf(Character( name = "myName", img = "image", occupation = arrayListOf(), status = "status", nickname = "nickName", appearance = arrayListOf() )) Mockito.`when`(dataRepository.getCharacters()) .thenReturn(Result.success(mockCharacters)) // 执行测试方法 mainActivityViewModel.fetchCharacters() // 验证调用和返回结果 verify(dataRepository).getCharacters() Mockito.verify(dataObserver).onChanged(Result.success(mockCharacters)) } }
优化建议(可选)
你当前ViewModel里硬编码了Dispatchers.IO,如果想要测试更稳定,可以把调度器也作为注入参数传入:
- 新增调度器的注入注解
- 在AppModule里提供对应调度器的实例
- 测试时直接传入
TestDispatcher即可,不需要依赖真实的IO调度器,测试速度更快,也不会有线程同步问题。
内容的提问来源于stack exchange,提问作者BRDroid
相关产品推荐
相关产品推荐

