如何从 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 进行单元测试?

提前致谢。

经过多次尝试,我无法轻易地做到这一点而不会出现奇怪的行为。所以我重构了我的函数以分别使用 ChannelCoroutineScope。感谢 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)
    ...
}