Ktor 客户端单元测试在 CoroutineScope.launch() 内挂起/冻结

Ktor client unit test hangs / freezes within CoroutineScope.launch()

我的 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 模拟器上时,我的代码似乎工作正常。 它仅在 运行 作为单元测试时挂起

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

我的解决方案最终是 运行 我在 运行Blocking() 范围内的测试,并将其 coroutineContext 注入我的 class 以供使用当 运行宁 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")
}

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