Kotlin流:测试挂起
我正在尝试使用流测试Kotlin实现。我使用Kotest进行测试。此代码适用于: 视图模型:Kotlin流:测试挂起,kotlin,kotlin-coroutines,kotlin-flow,kotlin-coroutines-flow,kotest,Kotlin,Kotlin Coroutines,Kotlin Flow,Kotlin Coroutines Flow,Kotest,我正在尝试使用流测试Kotlin实现。我使用Kotest进行测试。此代码适用于: 视图模型: val detectedFlow = flow<String> { emit("123") delay(10L) emit("123") } 但是,在real ViewModel中,我需要向流中添加值,因此我使用ConflatedBroadcastChannel,如下所示: private val _detectedValues = ConflatedBroadca
val detectedFlow = flow<String> {
emit("123")
delay(10L)
emit("123")
}
但是,在real ViewModel中,我需要向流中添加值,因此我使用ConflatedBroadcastChannel
,如下所示:
private val _detectedValues = ConflatedBroadcastChannel<String>()
val detectedFlow = _detectedValues.asFlow()
suspend fun sendDetectedValue(detectedString: String) {
_detectedValues.send(detectedString)
}
测试只是挂起,永远不会完成。我尝试了各种方法:launch
或runBlockingTest
而不是runBlockingTest
,将发送和收集放在相同或单独的协同程序中,offer
而不是send
。。。似乎没有什么能解决它。我做错了什么
更新:如果我手动创建流,它会工作:
private val _detectedValues = ConflatedBroadcastChannel<String>()
val detectedFlow = flow {
this.emit(_detectedValues.openSubscription().receive())
}
private val_detectedValues=confollatedbroadcastchannel()
val detectedFlow=流量{
this.emit(_detectedValues.openSubscription().receive())
}
那么,它是asFlow()
方法中的一个bug吗?问题是,您在测试中使用的函数是一个挂起函数,它将挂起执行,直到流
完成
在第一个示例中,您的detectedFlow
是有限的。它只会发出两个值并完成。在您的问题更新中,您还创建了一个有限流,它将发出一个值并完成。这就是为什么你的测试有效
但是,在第二个(现实生活)示例中,流是从从不关闭的合并DBRoadCastChannel
创建的。因此,collect
函数将永远暂停执行。要使测试在不永久阻塞线程的情况下工作,还需要使流有限。对此,我通常使用first()
操作符。另一个选项是关闭合并的dbroadcast频道
,但这通常意味着仅仅因为测试不是一个好的实践,就要修改代码
这就是测试如何使用first()
操作符
"when the flow contains values they are emitted" {
val detectedString = "123"
val vm = ScanViewModel()
runBlocking {
vm.sendDetectedValue(detectedString)
}
runBlocking {
vm.detectedFlow.first() shouldBe detectedString
}
}
你试过在其他调度器上启动吗,只是为了调试?是的。没有效果。顺便说一句,我尝试了新发布的StateFlow/MutableStateFlow,但问题仍然存在,如果我们的整个测试函数被标记为runBlocking
?@IgorGanapolsky,我相信在这种情况下整个测试函数也会挂起。我们如何处理生产代码在流和通道上使用collect
的情况?@TomášHavlíčekcollect
不会挂起,因为它是异步的
private val _detectedValues = ConflatedBroadcastChannel<String>()
val detectedFlow = flow {
this.emit(_detectedValues.openSubscription().receive())
}
"when the flow contains values they are emitted" {
val detectedString = "123"
val vm = ScanViewModel()
runBlocking {
vm.sendDetectedValue(detectedString)
}
runBlocking {
vm.detectedFlow.first() shouldBe detectedString
}
}