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

使用Flow+Firestore的Android Repository层集成测试await超时问题

问题根因

Firebase Task 的完成回调默认会被调度到Android主线程执行,你的测试父类已将Dispatchers.Main替换为TestCoroutineDispatcher,而你在测试中使用普通runBlocking执行代码时,TestCoroutineDispatcher默认不会自动执行队列中的待处理任务,导致Task的完成回调永远不会被调用,kotlinx-coroutines-play-services提供的await()扩展会一直等待回调触发,因此出现永久阻塞。

解决方案

方案1:使用父类封装的runBlockingTest执行测试逻辑

替换测试方法中的runBlocking为父类已经封装好的runBlockingTest,它会自动调度TestCoroutineDispatcher队列中的任务,确保Task的回调可以正常执行:

@ExperimentalTime
@Test
fun `#getExercises returns a flow of exercises`() = runBlockingTest {
    val exercises = mutableListOf<Exercise>().apply {
        add(Exercise("a", "pushups"))
        add(Exercise("b", "pull-ups"))
        add(Exercise("c", "sit-ups"))
    }

    val task1 = firestoreInstance.collection(EXERCISES_REF).add(exercises.first())
    val task2 = firestoreInstance.collection(EXERCISES_REF).add(exercises[1])
    val task3 = firestoreInstance.collection(EXERCISES_REF).add(exercises.last())

    Tasks.await(task1)
    Tasks.await(task2)
    Tasks.await(task3)

    println("Done with tasks: task1: ${task1.isComplete}. task2: ${task2.isComplete}. task3: ${task3.isComplete}.")

    println("About to get exercises")

    subject.getExercises().test(timeout = Duration.seconds(5)) {
        println("test body")
        assertThat(awaitItem().size, `is`(3))
    }
}

方案2:为Flow指定独立的IO调度器

在Repository的Flow构造时指定flowOn(Dispatchers.IO),让Task的回调执行在不受测试调度器控制的IO线程上,避开测试主线程调度器的限制:

fun getExercises() = flow<List<Exercise>> {
    println("beginning of searchForExercise")
    val exercises = exerciseCollRef.limit(5).get().await()
    println("Exercise count: ${exercises.documents}")
    emit(exercises.toObjects(Exercise::class.java))
}.flowOn(Dispatchers.IO) // 新增这行

方案3:替换为阻塞式Tasks.await

如果不想调整调度器配置,可以把kotlin扩展的await()替换为Google官方提供的阻塞式Tasks.await方法,它不依赖协程调度器的回调触发,直接阻塞当前线程等待Task完成:

fun getExercises() = flow<List<Exercise>> {
    println("beginning of searchForExercise")
    // 替换await()为Tasks.await,需要运行在IO调度器避免阻塞主线程
    val exercises = Tasks.await(exerciseCollRef.limit(5).get())
    println("Exercise count: ${exercises.documents}")
    emit(exercises.toObjects(Exercise::class.java))
}.flowOn(Dispatchers.IO)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 14:06:03