如何使用协程对 Kotlin-JS 代码进行单元测试?



>我创建了一个多平台Kotlin项目(JVM&JS),声明了一个预期的类并实现了它:

// Common module:
expect class Request(/* ... */) {
suspend fun loadText(): String
}
// JS implementation:
actual class Request actual constructor(/* ... */) {
actual suspend fun loadText(): String = suspendCoroutine { continuation ->
// ...
}
}

现在我正在尝试使用kotlin.test进行单元测试,对于 JVM 平台,我只是使用这样的runBlocking

@Test
fun sampleTest() {
val req = Request(/* ... */)
runBlocking { assertEquals( /* ... */ , req.loadText()) }
}

如果没有runBlocking,如何在JS平台上重现类似的功能?

Mb 已经晚了,但是在 js-tests 中添加使用suspend函数的可能性存在悬而未决的问题(此函数将透明转换为承诺)

解决方法

可以在通用代码中定义:

expect fun runTest(block: suspend () -> Unit)

在 JVM 中实现的

actual fun runTest(block: suspend () -> Unit) = runBlocking { block() }

在 JS 中

actual fun runTest(block: suspend () -> Unit): dynamic = promise { block() } 

TL;博士

  1. 在JS上可以使用GlobalScope.promise { ... }
  2. 但对于大多数用例,最好的选择可能是使用runTest { ... }(从kotlinx-coroutines-test),它是跨平台的,并且与runBlocking { ... }GlobalScope.promise { ... }相比还有一些其他好处。

完整答案

我不确定最初发布问题时的情况如何,但现在运行使用suspend函数的测试的标准跨平台方法是使用runTest { ... }(从kotlinx-coroutines-test开始)。

请注意,除了在所有平台上运行之外,这还包括一些其他功能,例如跳过delays(具有模拟时间流逝的能力)。

如果出于任何原因(这不是典型的,但有时可能是这种情况),实际上希望在测试中运行代码,因为它在生产中运行(包括实际的delay),那么runBlocking { ... }可以在JVM和Native上使用,GlobalScope.promise { ... }在JS上使用。如果选择此选项,定义一个在 JVM 和 Native 上使用runBlocking,在 JS 上使用GlobalScope.promise的函数签名可能很方便,例如:

// Common:
expect fun runTest(block: suspend CoroutineScope.() -> Unit)
// JS:
@OptIn(DelicateCoroutinesApi::class)
actual fun runTest(block: suspend CoroutineScope.() -> Unit): dynamic = GlobalScope.promise(block=block)
// JVM, Native:
actual fun runTest(block: suspend CoroutineScope.() -> Unit): Unit = runBlocking(block=block)

我能够进行以下工作:

expect fun coTest(timeout: Duration = 30.seconds, block: suspend () -> Unit): Unit
// jvm
actual fun coTest(timeout: Duration, block: suspend () -> Unit) {
runBlocking {
withTimeout(timeout) {
block.invoke()
}
}
}
// js
private val testScope = CoroutineScope(CoroutineName("test-scope"))
actual fun coTest(timeout: Duration, block: suspend () -> Unit): dynamic = testScope.async {
withTimeout(timeout) {
block.invoke()
}
}.asPromise()

这会使用async在您选择的范围内启动一个协程,然后您可以像承诺一样返回该协程。

然后,您编写如下测试:

@Test
fun myTest() = coTest {
...
}

相关内容

  • 没有找到相关文章

最新更新