1

我正在尝试使用该mockk框架在我的一个单元测试中设置一个模拟,该单元测试执行如下挂起函数:

val executionCompletionSource = CompletableDeferred<Nothing>()
suspend fun task(): Unit = executionCompletionSource.await()
val mock = mockk<Executable> { coEvery { execute() } coAnswers { task() } }

mock.execute()但是,如果我在启动的协程范围内调用,我发现测试会无限期挂起。如果我task()直接在启动范围内调用,则测试运行良好。

尽管mockk 文档确实谈到了一些关于 coroutine mocking 的内容,但我找不到任何文档或示例来展示如何执行 coroutines 以响应在 mock 上调用挂起函数。

这是一个 SSCCE 展示了这一点:

package experiment

import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.system.measureTimeMillis

interface Executable {
    suspend fun execute()
}

fun main() {
    val executionCompletionSource = CompletableDeferred<Nothing>()
    suspend fun task(): Unit = executionCompletionSource.await()
    val mock = mockk<Executable> { coEvery { execute() } coAnswers { task() } }

    runBlocking {
        val execution = launch { mock.execute() } // This blocks the test indefinitely
        //val execution = launch { task() } // This works fine (~110-120 ms)
        measureTimeMillis {
            delay(100)
            executionCompletionSource.cancel()
            execution.join()
        }.also { elapsed -> println("Elapsed in $elapsed ms") }
    }
}

使用以下依赖项:

implementation(kotlin("stdlib-jdk8"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0")
implementation("io.mockk:mockk:1.8.10.kotlin13")
4

1 回答 1

0

这似乎是 mockk 处理协程的方式的错误 - 现在正在此处对其进行跟踪

作为一种临时解决方法,我在需要模拟挂起功能的情况下手动创建模拟。例如:

private fun generateDummyChildren(
    numberOfChildren: Int = 5 /* sensible default */,
    executionRoutine: suspend () -> Unit
): Iterable<ExecutableNode> {
    fun createDummy(index: Int) = object: ExecutableNode {
        override val id = "Dummy $index"
        override suspend fun execute() = executionRoutine()
    }

    return Array(numberOfChildren, ::createDummy).asIterable()
}
于 2018-11-09T12:47:51.020 回答