如何从 Kotlin Flow 中的 callbackFlow 构建器模拟 ProducerScope?
How to mock a ProducerScope from callbackFlow builder in Kotlin Flow?
我想测试一个使用 callbackFlow
构建器作用域的函数。假设我在流生成器中有一个这样的函数:
fun items(): Flow<Items> = callbackFlow {
getItems(this) {
trySend(it)
}
awaitClose()
}
在getItems
函数中,我从websockets接收数据。 ProducerScope 的作用域用于 launch
延迟的新协程并执行某些操作,或者用于 close
发生错误时的作用域。所以它可能会调用 scope.launch { }
或 scope.close()
.
例如,这可以做如下事情:
fun getItems(scope: ProducerScope<Items>, callback: (Items) -> Unit) {
if (something) {
scope.launch { ... }
}
if (somethingElse) {
...
scope.close(error)
}
...
callback(items)
}
callbackFlow 的块使用 ProducerScope
, extension of CoroutineScope
and SendChannel
, I tried to mock it using Mockk:
val scope: ProducerScope<Items> = mockk()
不幸的是,我最终得到:
java.lang.ClassCastException: class kotlin.coroutines.CoroutineContext$Element$Subclass6 cannot be cast to class kotlin.coroutines.ContinuationInterceptor
如何模拟 ProducerScope
?
当 scope
可以是 CoroutineScope 和 SendChannel 时,如何对上面的 getItems
进行单元测试?
提前致谢。
经过多次尝试,我无法轻易地做到这一点而不会出现奇怪的行为。所以我重构了我的函数以分别使用 Channel
和 CoroutineScope
。感谢 the CoroutineScope plus
extension,我可以从流构建器创建一个新范围。现在可以测试了!
因此,流量生成器变成了:
fun items(): Flow<Items> = callbackFlow {
val channel = this.channel
val scope = this.plus(this.coroutineContext)
getItems(channel, scope) {
...
}
...
}
我的函数仍然使用两者,但分别获取它们:
fun getItems(
channel: SendChannel<Items>,
scope: CoroutineScope,
callback: (Items) -> Unit
) {
if (something) {
scope.launch { ... } // <-- use scope
}
if (somethingElse) {
...
channel.close(error) // <-- use channel
}
...
callback(items)
}
然后,我现在可以使用 Channel
进行测试,其要求与 callbackFlow
中的要求相同,范围来自 runTest
:
@Test
fun `get items and succeed`() = runTest {
val channel = Channel<Any>(Channel.BUFFERED, BufferOverflow.SUSPEND)
...
service.getItems(channel, this@runTest, callback)
...
}
我想测试一个使用 callbackFlow
构建器作用域的函数。假设我在流生成器中有一个这样的函数:
fun items(): Flow<Items> = callbackFlow {
getItems(this) {
trySend(it)
}
awaitClose()
}
在getItems
函数中,我从websockets接收数据。 ProducerScope 的作用域用于 launch
延迟的新协程并执行某些操作,或者用于 close
发生错误时的作用域。所以它可能会调用 scope.launch { }
或 scope.close()
.
例如,这可以做如下事情:
fun getItems(scope: ProducerScope<Items>, callback: (Items) -> Unit) {
if (something) {
scope.launch { ... }
}
if (somethingElse) {
...
scope.close(error)
}
...
callback(items)
}
callbackFlow 的块使用 ProducerScope
, extension of CoroutineScope
and SendChannel
, I tried to mock it using Mockk:
val scope: ProducerScope<Items> = mockk()
不幸的是,我最终得到:
java.lang.ClassCastException: class kotlin.coroutines.CoroutineContext$Element$Subclass6 cannot be cast to class kotlin.coroutines.ContinuationInterceptor
如何模拟 ProducerScope
?
当 scope
可以是 CoroutineScope 和 SendChannel 时,如何对上面的 getItems
进行单元测试?
提前致谢。
经过多次尝试,我无法轻易地做到这一点而不会出现奇怪的行为。所以我重构了我的函数以分别使用 Channel
和 CoroutineScope
。感谢 the CoroutineScope plus
extension,我可以从流构建器创建一个新范围。现在可以测试了!
因此,流量生成器变成了:
fun items(): Flow<Items> = callbackFlow {
val channel = this.channel
val scope = this.plus(this.coroutineContext)
getItems(channel, scope) {
...
}
...
}
我的函数仍然使用两者,但分别获取它们:
fun getItems(
channel: SendChannel<Items>,
scope: CoroutineScope,
callback: (Items) -> Unit
) {
if (something) {
scope.launch { ... } // <-- use scope
}
if (somethingElse) {
...
channel.close(error) // <-- use channel
}
...
callback(items)
}
然后,我现在可以使用 Channel
进行测试,其要求与 callbackFlow
中的要求相同,范围来自 runTest
:
@Test
fun `get items and succeed`() = runTest {
val channel = Channel<Any>(Channel.BUFFERED, BufferOverflow.SUSPEND)
...
service.getItems(channel, this@runTest, callback)
...
}