渐进式编程课
KMP 目录
第 47 课2026.09.04

验证最后一个订阅者离开后,上游是否按设定超时停止。

第 47 课:WhileSubscribed停止时机测试

  • 日期:2026-09-04
  • 课程序号:第 47 课
  • 知识点:虚拟时间与上游取消

用途

验证最后一个订阅者离开后,上游是否按设定超时停止。

核心概念

stopTimeoutMillis 延迟停止上游,不是延迟发值;runCurrent 执行当前时刻任务。backgroundScope 在测试结束时自动取消,避免共享任务悬挂。

最小代码或操作示例

kotlin
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.*

@OptIn(ExperimentalCoroutinesApi::class)
class SharingStopTest {
    @Test
    fun stopsAfterLastSubscriberTimeout() = runTest {
        var active = false
        val shared = flow<Int> {
            active = true
            try { emit(1); awaitCancellation() }
            finally { active = false }
        }.shareIn(backgroundScope, SharingStarted.WhileSubscribed(1000), replay = 1)
        val subscriber = launch { shared.collect { } }
        runCurrent()
        assertTrue(active)
        subscriber.cancel()
        runCurrent()
        advanceTimeBy(999)
        runCurrent()
        assertTrue(active)
        advanceTimeBy(1)
        runCurrent()
        assertFalse(active)
    }
}

3~5 分钟练习

放入沿用上一课协程测试依赖的 commonTest。将超时改成 0,把取消后的期望改为立即停止。

参考答案

subscriber.cancel() 后调用 runCurrent(),此时应 assertFalse(active)。删除原先等待 999 毫秒后仍 active 的断言;runCurrent 不代表等待现实时间。

与上一课的联系

上一课确保订阅先建立,本课继续控制测试调度,观察订阅结束后的上游生命周期。

参考资料