Reputation: 338
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.
Upvotes: 2
Views: 4214
Reputation: 462
You can combine the static mocking and capture capabilities of mockk to validate e.g. a POST request payload etc. along these lines:
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.slot
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpRequest.BodyPublishers
import java.net.http.HttpResponse
import java.nio.charset.StandardCharsets
val httpClient = mockk<HttpClient>()
val httpResponse = mockk<HttpResponse<String>>()
every { httpResponse.body() } returns "ignored"
every { httpResponse.statusCode() } returns 200
val httpRequestCapturingSlot = slot<HttpRequest>()
every {
httpClient.send(
capture(httpRequestCapturingSlot),
HttpResponse.BodyHandlers.ofString()
)
} returns httpResponse
val expectedRequestJson = Gson().toJson(
<your expected payload object>
)
mockkStatic(BodyPublishers::class)
val postBodyCapturingSlot = slot<String>()
every { BodyPublishers.ofString(capture(postBodyCapturingSlot)) } returns BodyPublishers.ofString(
"stand-in only",
StandardCharsets.UTF_8
)
// make SUT call resulting in HttpRequest
val contentHeader = "Content-Type" to listOf("application/json")
val httpRequest = httpRequestCapturingSlot.captured
httpRequest.method() shouldBe "POST"
httpRequest.uri().toString() shouldBe <expected uri>
httpRequest.headers().map() shouldContain contentHeader
postBodyCapturingSlot.captured shouldBe expectedRequestJson
Upvotes: 1
Reputation: 3390
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?
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")
}
Upvotes: 2