首页 > 解决方案 > 单元测试在执行期间冻结

问题描述

所以,我的问题在这里 - 我正在尝试为我的应用程序进行单元测试。我有 2 项服务,我们称它们为 Foo 和 Bar,而 Foo 只不过是 Bar 的代理。

因此,Foo 服务的路由器如下所示:

fun fooRoute(...) = coRouter {
    . . .
    GET("/something", fooHandler::findSomething)
    . . .
}

向 Bar 服务发出请求的处理程序如下所示:

fun fooHandler(barClient: WebClient) {
    . . .
    suspend fun findSomething(request: ServerRequest): ServerResponse {
        val response = barClient.get()
            .uri(...)
            .accept(...)
            .awaitExchange()
        . . .
        return when (response.statusCode()) {
            HttpStatus.OK -> {
                ServerResponse
                    . . .
                    .bodyValueAndAwait(response.awaitBody<SomeType>())
            }
            else -> { 
                ServerResponse
                    . . .
                    .buildAndAwait()
            }
        }
    }
    . . .
}

当我这样写测试时:

. . .
private val barClient = mockk<WebClient>()

private val fooHandler = FooHandler(barClient)
private val fooRouter = FooRouter()
private val fooClient = WebTestClient.bindToRouterFunction(
    fooRouter.fooRoute(
        // mocks
        . . .
        fooHandler
    )
).build()

@Nested
inner class FindSomething {

    @Test
    fun `returns OK`() = runBlocking {

        val response = mockk<ClientResponse>()
        val spec = mockk<WebClient.RequestHeadersUriSpec<*>>()

        coEvery { response.awaitBody<SomeType>() } returns SomeType(...)
        coEvery { spec.awaitExchange() } returns response
        coEvery { barClient.get() } returns spec

        fooClient
            .get()
            .uri(...)
            .exchange()
            .expectStatus().is2xxSuccessful

        . . .
    }
}

它只是永远冻结......好吧,我认为这是因为它周围有一些协程魔法,但因为我还是这个新手,我不明白这里到底发生了什么。有什么帮助吗?

标签: unit-testingkotlinwebclientkotlin-coroutinesmockk

解决方案


Welp,我通过根据这个答案编写自己的交换函数解决了这个问题。这是我如何做到这一点的一个例子:

. . .
fun setUpBarClient(exchangeFunction: ExchangeFunction) {
    barClient = barClient.mutate()
        .exchangeFunction(exchangeFunction)
        .build()

    // recreate fooHandler & fooClient
}

@Nested
inner class FindSomething {

    @Test
    fun `returns OK`() {

        // this one from org.springframework.web.reactive.function.client.ExchangeFunction

        val exchangeFunction = ExchangeFunction {
            Mono.just(
                ClientResponse.create(HttpStatus.OK)
                    .header(...)
                    .body(...)
                    .build()
            )
        }

        setUpBarClient(exchangeFunction)

        fooClient
            .get()
            .uri(...)
            .exchange()
            .expectStatus().is2xxSuccessful

        . . .
    }
}

推荐阅读