如何使用Mockk模拟协程的执行?

时间:2018-11-09 09:34:11

标签: kotlin kotlinx.coroutines mockk

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

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

但是,我发现如果在已启动的协同程序范围内调用mock.execute(),则该测试将无限期挂起。如果我直接在启动的范围内调用task(),则测试运行正常。

尽管mockk documentation确实谈到了协程模拟,但我找不到任何文档或示例来说明如何执行协程以响应在模拟上调用暂停函数。

这是一个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")

1 个答案:

答案 0 :(得分:0)

这似乎是嘲笑mockk处理协程的方式-现在是tracked here

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

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()
}