如何在Kotlin中模拟HttpClient和HttpRequest进行单元测试?

67up9zun  于 2023-01-05  发布在  Kotlin
关注(0)|答案(1)|浏览(201)

我构建了一个Sping Boot 应用程序,它有一个名为FileDetails.kt的类。我试图在该类中测试的方法采用以下格式。

getFileDetails(auth: String, id: String): FileModel {
    val url = URI.create(“http://localhost:8080/$id”)
    val client = HttpClient.newBuilder().build()
    val request = HttpRequest.newBuilder().uri(url).header(“Authorization”, auth).build()
    val response = client.send(request, HttpResponse.BodyHandlers.ofString())
    return objectMapper.readValue(response.body(), object: TypeReference<FileModel>() {})}

我正在写一个单元测试来模拟当响应代码为200时,我们得到一个FileModel响应。这是我到目前为止所做的,但不确定如何继续。

@Test fun `test get file details method`() {
val response200 = Mockito.mock(HttpResponse::class.java)
val mockRequest = Mockito.mock(HttpRequest::class.java)
// not sure how to return mockRequest and response200}

我对此相当陌生,想知道是否有可能模仿这些React,以及如何进行。

mzmfm0qo

mzmfm0qo1#

你这样做是错误的。
如果你的目标只是模仿getFileDetails(),那么这很简单,你根本不需要为HttpResponse而烦恼。首先,假设你的fetcher在这样一个类中:

class Fetcher {

    fun getFileDetails(auth: String, id: String): FileModel {
        val url = URI.create("http://localhost:8080/$id")
        val client = HttpClient.newBuilder().build()
        val request = HttpRequest.newBuilder().uri(url).header("Authorization", auth).build()
        return objectMapper.readValue(response.body(), object: TypeReference<FileModel>() {})}
    }

}

你只需简单地写(抱歉,我使用的是https://mockk.io/而不是Mockito,因为Mockk是一个专门为Kotlin设计的mocking库,我更了解它-但概念/方法是相同的),

import io.mockk.every
import io.mockk.mockk

val fetcher = mockk<Fetcher>()

every { fetcher.fetchRawResponse(any(), any()) } returns FileModel(...)

如果你真的想用HttpRequest/Response在一个较低的层次上进行测试,你将需要分解getFileDetails(依赖注入风格)。

  1. HTTP请求的格式是否正确?
    1.从String到FileModel的反序列化是否正确?
    假设你只想测试(2)--我会远离测试任何Http的东西,并假设HttpClient的作者已经正确地测试了他们的代码,并像这样抽象你的外部调用:
class Fetcher {

    fun fetchRawResponse(auth: String, id: String) : HttpResponse<String> {
        val url = URI.create("http://localhost:8080/$id")
        val client = HttpClient.newBuilder().build()
        val request = HttpRequest.newBuilder().uri(url).header("Authorization", auth).build()
        return client.send(request, HttpResponse.BodyHandlers.ofString())
    }

}

然后调用者可以执行objectMapper.readValue(或者甚至更进一步,通过返回普通的String,不将HttpResponse对象泄漏出此方法)
假设您有一个类似上面的Fetcher类,下面介绍如何使用mock响应。

import io.mockk.every
import io.mockk.mockk
import io.mockk.verify

    @Test fun `test get file details method`() {
        val jsonResponse: HttpResponse<String> = mockk()
        every { jsonResponse.body() } returns  """
            {"some":"json"}
        """.trimIndent()
        val fetcher = mockk<Fetcher>()
        every { fetcher.fetchRawResponse(any(), any()) } returns jsonResponse
        // now you can call fetcher.fetchRawResponse("abc","def")
    }

相关问题