본문 바로가기

Programming/Java & JSP & Spring

[Spring] 외부 API 호출 서비스 테스트코드 작성하기 (WebClient & MockWebServer)

Spring에서는 API를 호출할 수 있는 많은 REST Client를 제공한다.

그 중에서도 WebClient가 가장 많이 추천되는 편이다.

 

Spring 팀에서도 RestTemplate의 대안으로 WebClient를 추천하고 있고, Reactive 환경과 MSA를 생각하고 있다면, WebClient는 좋은 대안이 될 것이다.

 

그럼 이러한 WebClient로 작성한 서비스는 어떻게 테스트 코드를 작성할 수 있을까?

두가지 방법이 존재한다.

 

  • Mockito를 사용하는 방법
  • 실제 WebClient를 사용하고, MockWebServer를 호출하는 방법

 

Mockito를 이용해 일일히 stubbing하는 방법도 있지만, 이 방법은 너무 번거롭고 복잡하다.

또한, 테스트하려는 서비스가 WebClient 동작방식에 대해 디테일하게 정의하고 mocking 해줘야하기 때문에 그리 좋은 방법이라고 볼 수 없다.

 

이 대안으로 MockWebServer를 활용한 테스트 방법이 있다.

MockWebServer를 연동하면, 테스트코드가 실제로 로컬에 있는 MockWebServer의 엔드포인트로 호출을 한다.

Spring 팀에서도 통합테스트 코드 작성 시 MockWebServer의 사용을 권장하고 있다.

 

 

그러면 MockWebServer의 사용법을 알아보자.

 

MockWebServer 라이브러리 추가

testImplementation("com.squareup.okhttp3:okhttp:4.9.1")
testImplementation("com.squareup.okhttp3:mockwebserver:4.9.1")

 

테스트 대상 Service 코드

@Service
class SampleService(
    private val webClient: WebClient
) {
    fun callExternalPostApi(request: String, externalApiUrl: String): String? {
        return webClient.post()
            .uri(externalApiUrl)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(request)
            .retrieve()
            .bodyToMono(String::class.java)
            .block()
    }
}

 

MockWebServer 초기화

val webClient = WebClient.create() // webClient 생성
val sampleService = SampleService(webClient = webClient) // 테스트 서비스 대상에 webclient 주입

lateinit var mockServer: MockWebServer // mockWebServer 초기화
beforeTest {
	mockServer = MockWebServer().also { it.start() }
}

afterTest {
	mockServer.shutdown()
}

 

MockWebServer 사용

describe("MockWebServer request checking") {
	val mockServerUrl = mockServer.url("/sample").toString() // set MockWebServer url
	val mockResponse = MockResponse()
		.setResponseCode(200)
		.setHeader("Content-Type", MediaType.APPLICATION_JSON)
		.setBody("성공")
	mockServer.enqueue(mockResponse)

	val result = sampleService.callExternalPostApi("요청값", mockServerUrl)
	val sampleServiceRequest = mockServer.takeRequest()

	result shouldBe "성공"
	sampleServiceRequest.method shouldBe "POST"
	sampleServiceRequest.path shouldBe "/sample"
	sampleServiceRequest.body.readUtf8() shouldBe "요청값"
}
  • `MockResponse()` : WebClient의 Response를 조작할 수 있다.
  • `mockServer.enqueue()` : MockWebServer는 queue 방식으로 응답하므로, 테스트 대상을 호출하기 전에 mocking 응답을 큐에 넣어준다.
  • `mockServer.takeRequest()` : WebClient를 호출할 때의 request를 capture할 수 있다.

 

위에서 사용된 샘플의 전체 코드는 여기서 확인할 수 있다.


- 전체 코드 : https://github.com/henry-jo/spring-mock-web-server

- 참조 : https://www.baeldung.com/spring-mocking-webclient