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만 다르게 했다.

돌려보면 성공하는 것을 확일할 수 있다.


참고

http://www.mock-server.com/

https://www.baeldung.com/mockserver

 

전체코드 : https://github.com/fpdjsns/DDD-practice/commit/17844bbb5eb82893b869a805afd9c93ca272cb6e

 

add MockServerNetty test · fpdjsns/DDD-practice@17844bb

Permalink Browse files add MockServerNetty test Loading branch information Showing 2 changed files with 161 additions and 9 deletions. +11 −9 build.gradle.kts +150 −0 src/test/kotlin/com/ddd/practice/MockServerNettyTest.kt @@ -1,12 +1,10 @@ import org.jetb

github.com

 

320x100