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

为什么AsyncPagingDataDiffer调用submitData会导致单元测试冻结超时?

问题产生的核心原因

  1. submitData 本身的挂起特性
    AsyncPagingDataDiffer.submitData() 是长期挂起的 suspend 方法,它会持续监听PagingData的分页加载事件、数据更新,除非协程被主动取消,否则不会主动返回。你直接在runTest的主线程调用它,测试会一直等待该方法返回,最终触发超时。你之前提交PagingData.from()构造的静态列表不会卡,是因为静态PagingData已预加载全部数据,没有后续分页任务,处理逻辑会快速走完。

  2. 协程调度器不匹配
    你构造PagingSourceFactory时指定了Dispatchers.Default,同时Paging组件内部默认会使用Android主调度器处理页面更新,而单元测试环境没有默认的主Looper,且runTest的虚拟时间控制逻辑默认不会调度其他调度器上的任务。从Room返回的PagingData是懒加载的,你调用first()只是拿到了流发射的PagingData外壳,实际的数据库查询、分页加载逻辑只有在调用submitData后才会触发,这些任务都跑在测试调度器管控外的线程上,永远不会被执行,导致submitData一直挂起。


解决方法

  1. 先添加调度器替换规则,把测试中用到的所有调度器都替换为测试调度器,确保runTest可以控制所有协程任务的执行:
@OptIn(ExperimentalCoroutinesApi::class)
class FooBarTest {
    @get:Rule
    val mainDispatcherRule = MainDispatcherRule() // 替换主调度器为TestDispatcher
}
// 同时构造PagingSourceFactory时,测试环境下注入TestDispatcher,不要写死Dispatchers.Default
  1. 把submitData放在单独的launch协程中执行,避免阻塞测试主线程,测试完成后主动取消该协程即可:
@ExperimentalCoroutinesApi
@Test
fun testGetFooBarsPaged(): Unit = runTest {
    // 省略differ初始化逻辑
    val pagingData = fooBarsSdk.getFooBarsPaged(FooBar.Type, pageSize = 1).first()
    
    // 单独启动协程执行submitData
    val submitJob = launch {
        differ.submitData(pagingData)
    }

    // 等待数据加载完成后执行断言
    while (differ.snapshot().isEmpty()) {
        delay(10) // 测试调度器下delay不会真的等待,只会推进虚拟时间
    }
    // 此处添加你的断言逻辑
    assertThat(differ.snapshot().size).isEqualTo(3)
    
    // 测试完成主动取消submitJob
    submitJob.cancel()
}
  1. 提前给测试用的内存Room数据库插入对应测试数据,避免PagingSource加载空数据一直重试。

内容的提问来源于stack exchange,提问作者J. Hill

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 10:15:08