Back-End (web)
[Test] webClient 통신 테스트 (mockServer)
햄과함께
2019. 12. 8. 21:41
320x100
// build.gradle
plugins{
// add
testImplementation("org.mock-server:mockserver-netty:5.7.2")
}
mockserver 의존성 추가
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
abstract class MockServerNettyHelper {
companion object {
val GSON = Gson()
}
private val host = "http://localhost"
private val port = 8000
lateinit var mockServer: ClientAndServer
@BeforeAll
fun startMockServer() {
// #1
mockServer = ClientAndServer.startClientAndServer(port)
}
@AfterAll
fun stopMockServer() {
// #2
mockServer.stop()
}
fun getWebClient(): WebClient {
return WebClient.builder()
.baseUrl("$host:$port")
.defaultHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.build()
}
fun setMockApi(path: String = "",
response: Any? = null,
params: MultiValueMap<String, String>? = null,
method: HttpMethod = HttpMethod.GET,
statusCode: HttpStatusCode = HttpStatusCode.OK_200,
delay: Long = 0) {
// #3
mockServer.`when`(HttpRequest.request().withMethod(method.toString())
.withPath(path)
.withQueryStringParameters(paramsToMockParams(params)))
.respond(HttpResponse.response()
.withStatusCode(statusCode.code())
.withHeader(CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.withBody(response?.let { GSON.toJson(it) } ?: null)
.withDelay(TimeUnit.SECONDS, delay))
}
private fun paramsToMockParams(params: MultiValueMap<String, String>?): List<Parameter> {
return params?.map { (u, v) ->
// #4
Parameter(NottableString.string(u), v.map { NottableString.string(it) })
}?.toList() ?: listOf()
}
}
#1 테스트 시작 전에 mock 서버를 켜준다.
#2 테스트들이 끝나면 mock 서버를 꺼준다.
#3 mockServer.when으로 특정 요청에 대한 mock response를 정의한다.
#4 map을 쿼리문자열로 만들어서 반환해준다. NottableString.string(str)은 str 문자열과 정확하게 맞는 요청이 올때만 응답값을 반환하고 싶은 경우 사용한다. 특정 문자열과 맞지 않는 경우를 판단하고 싶으면 NottableString.not(str)을 사용
class MockServerNettyTest : MockServerNettyHelper() {
data class TestResponse(val status: HttpStatus)
data class TestRequest(val id: String, val name: String) {
fun toParams() = LinkedMultiValueMap<String, String>().apply {
set("id", id)
set("name", name)
}
}
private val path = "/success"
private val request = TestRequest("id", "withham")
private val response = TestResponse(HttpStatus.OK)
private val client by lazy { getWebClient() }
private val failRequest = TestRequest("withham", "IDDID")
@BeforeAll
fun setUp() {
// set dummy api response
setMockApi(path, response, request.toParams())
setMockApi(path, null, failRequest.toParams(), HttpMethod.GET,
HttpStatusCode.INTERNAL_SERVER_ERROR_500)
}
@Test
fun successTest() {
// when
val actual = client.get()
.uri { uriBuilder ->
uriBuilder.path(path)
.queryParams(request.toParams())
.build()
}
.retrieve()
.bodyToMono(TestResponse::class.java)
// then
StepVerifier.create(actual)
.expectNext(response)
.expectComplete()
.verify()
}
@Test
fun statusFailTest() {
// when
val actual = client.get()
.uri { uriBuilder ->
uriBuilder.path(path)
.queryParams(failRequest.toParams())
.build()
}
.exchange()
.map { response ->
if (response.statusCode().isError) {
TestResponse(response.statusCode())
} else {
response.bodyToMono(TestResponse::class.java)
} as TestResponse
}
// then
StepVerifier.create(actual)
.expectNextMatches { response ->
response.status == HttpStatus.INTERNAL_SERVER_ERROR
}
.expectComplete()
.verify()
}
}
테스트 코드는 위와 같이 작성했다.
MockServerNettyHelper를 상속받는 클래스를 만들었다.
Helper에서 mockServer를 키기 때문에 @BeforeAll에 mockApi에 대한 설정을 해야한다.
Helper에서 webClient를 가져와서 해당 webClient로 호출 테스트를 했다.
테스트코드는 간단하게 성공, 실패 2개 테스트 코드만 작성했다.
호출 분기는 path는 같고 queryParam만 다르게 했다.
돌려보면 성공하는 것을 확일할 수 있다.
참고
https://www.baeldung.com/mockserver
전체코드 : https://github.com/fpdjsns/DDD-practice/commit/17844bbb5eb82893b869a805afd9c93ca272cb6e
320x100