【问题标题】:Ktor client unit test hangs / freezes within CoroutineScope.launch()Ktor 客户端单元测试在 CoroutineScope.launch() 中挂起/冻结
【发布时间】:2023-03-22 18:34:02
【问题描述】:

我的 KMM 项目测试在新范围内异步启动的 ktor 客户端请求时遇到问题。出于测试目的,我传入Dispatchers.Unconfined 作为新范围的上下文(在实际生产代码中我使用newSingleThreadContext())。

我在下面创建了一个非常简化的悬挂 ktor 请求版本:

@ExperimentalCoroutinesApi
@Test
fun testExample(): Unit {
    val scope = CoroutineScope(Dispatchers.Unconfined)
    scope.launch {
        val client = HttpClient { BrowserUserAgent() }

        // This line hangs
        val response : HttpResponse = client.get("https://google.com")

        // Will never get here
        println("Response: $response")
        fail("This test should fail")
    }
}

请注意,如果您不在 CoroutineScope.launch 中调用,那么它可以正常工作。然后挂起/冻结仅在 CoroutineScope.launch 中调用时发生。同样,这是一个极其简化的示例,但在我的实际代码中,以这种方式设置它的原因是,我可以在最终发出 ktor 请求之前在后台线程中处理一些数据 - 因此是 CoroutineScope.launch。另请注意,我的代码在 iOS 模拟器上运行时似乎运行良好。 只有在作为单元测试运行时才会挂起

我是否遗漏了一些东西来完成这项工作,或者这是一个错误?

【问题讨论】:

  • 在我的机器上,您的测试在 iosX64 和 linuxX64 目标上使用 Ktor 1.6.4 或 1.6.5 通过。你能描述一下你运行这个测试的环境吗?
  • 对我来说测试也通过了,但这不是问题。问题是响应永远不会返回,println("Response: $response") 行永远不会打印。我编辑了代码示例以添加fail() 语句,以便测试用例应该失败 - 但是在我的机器上它仍然通过。在 MacOS 上运行,目标为 android 和 iosX64,ktor v1.6.5,kotlin v1.5.31,kotlinx-coroutines-core v1.5.2-native-mt。
  • 这是预期的行为,因为 launch 块中的代码是异步执行的。由于您没有明确等待其完成,因此永远不会打印响应。换句话说,您的测试在后台作业之前完成了它的执行。

标签: kotlin-coroutines ktor kotlin-multiplatform-mobile


【解决方案1】:

我的解决方案最终是在 runBlocking() 范围内运行我的测试,并将其 coroutineContext 注入我的类中,以便在运行 CoroutineScope.launch() 时使用。

object RequestService {
  private val requestContext = newSingleThreadContext("request")
  var testContext by AtomicReference<CoroutineContext?>(null)

  fun makeRequest(block : () -> Unit) {
    CoroutineScope.launch(testContext ?: requestContext) {
      block()
    }
  }
}

@Test
fun testExample() = runBlocking {
    RequestService.testContext = this.coroutineContext
    RequestService.makeRequest() {
        println("Running Ktor coroutine test")
        val client = HttpClient { BrowserUserAgent() }

        // This line doesn't hang anymore when running within runBlocking coroutineContext
        val response : HttpResponse = client.get("https://google.com")
        println("Response: $response")
    }
    println("Finished running Ktor coroutine test")
}

希望这有助于尝试创建跨平台可测试服务的其他人。感谢Aleksei 的评论帮助我到达那里。

【讨论】:

    猜你喜欢
    • 2016-01-01
    • 2021-09-25
    • 2021-08-02
    • 1970-01-01
    • 1970-01-01
    • 2013-12-07
    • 1970-01-01
    • 1970-01-01
    • 2022-09-23
    相关资源
    最近更新 更多