You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,如果想要测试更稳定,可以把调度器也作为注入参数传入:

  1. 新增调度器的注入注解
  2. 在AppModule里提供对应调度器的实例
  3. 测试时直接传入TestDispatcher即可,不需要依赖真实的IO调度器,测试速度更快,也不会有线程同步问题。

内容的提问来源于stack exchange,提问作者BRDroid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 15:48:04