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

Android Kotlin中runTest测试ViewModel时验证失败求助

JUnit4 + Kotlin 1.8.10中runTest测试ViewModel协程逻辑失败的问题

问题场景

  • 测试LoadingViewModel的checkXyz方法时,遇到runTest验证失败的问题
  • checkXyz内部通过viewModelScope.launch(后续指定了ioDispatcher)执行delay(1000L)后将xyz设为true
  • 测试用spyk创建ViewModel实例,在runTest中调用checkXyz后,验证subject.xyz = true失败;移除delay测试正常,指定ioDispatcher后问题依旧

错误信息

Verification failed: call 1 of 1: LoadingViewModel(#3).setXyz(eq(true))) was not called.
Calls to same mock:

  1. LoadingViewModel(#3).checkXyz()
  2. LoadingViewModel(#3).getTag(androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY)
  3. LoadingViewModel(#3).setTagIfAbsent(androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY, androidx.lifecycle.CloseableCoroutineScope@2d5580f6)

解决思路与方案

问题核心是viewModelScope的真实调度器与runTest的测试调度器未对齐,导致测试代码在协程完成delay逻辑前就执行了验证。

方案1:给ViewModel注入可替换的调度器

修改ViewModel构造,允许传入自定义调度器:

class LoadingViewModel(
    private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) : ViewModel() {
    var xyz = false
    
    fun checkXyz() {
        viewModelScope.launch(dispatcher) {
            delay(1000L)
            xyz = true
        }
    }
}

测试时使用测试调度器,并推进到所有任务完成:

@Test
fun testCheckXyz() = runTest {
    val testDispatcher = UnconfinedTestDispatcher(testScheduler)
    val viewModel = spyk(LoadingViewModel(testDispatcher))
    
    viewModel.checkXyz()
    advanceUntilIdle() // 让测试调度器执行完所有挂起任务
    
    verify { viewModel.xyz = true }
}

方案2:手动替换viewModelScope的调度器

若无法修改ViewModel构造,可在测试中替换viewModelScope的调度器:

@Test
fun testCheckXyz() = runTest {
    val viewModel = spyk(LoadingViewModel())
    // 替换viewModelScope为绑定测试调度器的作用域
    val testScope = CloseableCoroutineScope(UnconfinedTestDispatcher(testScheduler))
    viewModel.setTagIfAbsent(ViewModelCoroutineScope.JOB_KEY, testScope)
    
    viewModel.checkXyz()
    advanceUntilIdle()
    
    verify { viewModel.xyz = true }
}

关键注意点

  • 必须调用advanceUntilIdle()或advanceTimeBy(1000L),确保测试调度器执行完delay后的逻辑,否则验证会在协程挂起时提前执行
  • 测试中避免直接使用真实调度器(如Dispatchers.IO),这类调度器不会被runTest自动接管,需手动替换为测试调度器

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 11:32:40