简体   繁体   中英

How to mock HttpClient and HttpRequest for unit test in kotlin?

I have built a spring boot application that has a class called FileDetails.kt. The method that I am trying to test, in the class, is in the following format.

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>() {})}

I am writing a unit test to mock that when the response code is 200, we get a FileModel response. This is what I have done so far but unsure how to proceed.

@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}

I am fairly new this and would like to know if it is possible to mock these responses and how to go about that.

You are going about this the wrong way.

If your aim is just to mock getFileDetails() then this is easy enough and you don't need to be bothered with HttpResponse at all. Firstly, suppose your fetcher was in a class like this:

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>() {})}
    }

}

you simply write (and apologies I am using https://mockk.io/ not Mockito since Mockk is a mocking library specifically for Kotlin which I know better - but the concepts / approach is the same),

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

val fetcher = mockk<Fetcher>()

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

If you really wanted to test at a lower level with HttpRequest/Response, you will need to break down getFileDetails (Dependency Injection style). What is it you are trying to test?

  1. that the HTTP request is properly formed?
  2. that the deserialization from String to FileModel is correct?

Suppose it was just (2) you wanted to test - I would keep away from testing any Http stuff and assume that the authors of HttpClient have tested their code properly, and abstract your external calls something like this:

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())
    }

}

Then the caller can do the objectMapper.readValue (or even go further and don't leak the HttpResponse object out of this method, by returning a plain String )

Supposing you had a Fetcher class like the above, this is how to use mock the response.

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")
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM