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

Android Compose:测试PagerState的animateScrollToPage遇异常如何解决?

解决Accompanist Pager animateScrollToPage测试时的MonotonicFrameClock异常

问题根源

animateScrollToPage是依赖MonotonicFrameClock逐帧驱动的挂起函数,而Compose测试环境的默认协程上下文并不自带这个时钟。你之前用rememberCoroutineScope拿到的协程上下文仅绑定Compose运行时,但在runOnIdle中启动协程时,上下文丢失了FrameClock,导致异常抛出。

解决方案

以下两种方法都能解决这个问题,任选其一即可:

方法一:使用TestFrameClock手动驱动动画

import kotlinx.coroutines.test.TestFrameClock
import kotlinx.coroutines.runBlocking

// 初始化测试用帧时钟
val testClock = TestFrameClock()

composeTestRule.setContent {
    // 给CoroutineScope注入测试帧时钟
    val scope = rememberCoroutineScope { testClock + it.coroutineContext }
    val pagerState = rememberPagerState()
    // 渲染Pager组件...
}

composeTestRule.runOnIdle {
    runBlocking(testClock) {
        // 启动动画
        scope.launch { pagerState.animateScrollToPage(page = 8) }
        // 推进时钟,给动画足够时间完成(比如1秒)
        testClock.advanceTimeBy(1000)
    }
    
    // 执行断言
    assertThat(pagerState.currentPage).isEqualTo(8)
    assertThat(pagerState.pageCount).isEqualTo(10)
}

方法二:利用Compose测试库自带的mainClock

composeTestRule.setContent {
    val pagerState = rememberPagerState()
    // 渲染Pager组件...
}

composeTestRule.runOnIdle {
    // 关闭自动时钟推进,手动控制动画进度
    composeTestRule.mainClock.autoAdvance = false
    
    runBlocking {
        // 启动动画
        launch { pagerState.animateScrollToPage(page = 8) }
        // 推进时钟至动画完成
        composeTestRule.mainClock.advanceTimeBy(1000)
    }
    
    // 执行断言
    assertThat(pagerState.currentPage).isEqualTo(8)
    assertThat(pagerState.pageCount).isEqualTo(10)
}

关键注意事项

  • 必须确保动画完全执行完毕后再断言,否则currentPage可能处于中间状态导致断言失败,所以要推进足够的时间(一般1秒足够覆盖Pager动画)。
  • 不要在runOnIdle中直接使用rememberCoroutineScope的协程启动动画,因为该上下文无法在测试环境中获取到FrameClock。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 04:51:12