WireMock을 이용한 API 서버 Test
- 다양한 프로젝트를 진행하다보면 Server To Server 통신을 진행해야만 하는 상황들이 발생한다(소셜로그인, MSA 아키텍처구조 ...)
이럴때 테스트 대상이되는 모든 서버를 Local 에 구동시킨채 Test 를 진행하기에는 많은 자원낭비, 많은 시간소요 등 불편한점이 많다.
특히 Test Code 를 작성해서 테스트마저 자동화를 많이 하고 있는 추세이기 때문이다.
WireMock 이란
- Http 기반의 API 서비스를 Mocking 하기 위한 라이브러리이다.
즉, 외부 서비스에 의존하는 테스트를 진행할때, 외부 서비스의 Response(응답)을 Mocking 하여 테스트를 진행할 수 있게 해준다.
(외부 서비스에서 받아올 응답을 미리 지정해둔 형태로 Test 진행)
테스트 단계중 단순 외부서비스와의 통신을 통해 진행해야 하는 경우, 의존하지 않고 진행할 수 있다는점에서 테스트하기에 용이하다고 생각하여 적용하게 되었다.
WireMock 사용 방법
일반적으로 WireMock 을 사용하는 방법에는 2가지 정도가 존재한다.
- Docker, 또는 Local 에 별도 서버를 구동하는 Standalone 방식(Jar 파일 사용)
- Test Code 를 작성하지 않고 사용할때 사용
- Spring에 의존성을 추가하여, JUnit 을 사용한 테스트시 진행하는 방법
해당 글에서는 2번째 방법인 JUnit 을 통해 Test 를 진행해 보도록 하겠다.
WireMock 적용
일반적으로 의존성을 추가하듯이 build.gradle 에 의존성을 추가해주면 된다.
testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-stub-runner:4.1.4'
Version?
- WireMock 이나 다른 라이브러리들과 같이 공식 문서를 통해 사용하는 Spring 버전별로 버전을 맞춰주면 되는데
현재 가장 최신 버전은 4.1.4 버전이다. (4.2.0-M1 정식 릴리즈가 아닌 버전이 존재하긴한다. 2024.10 기준)
spring Webflux 3.x 버전 부터는 4.0.5 버전부터 지원 가능하다!
https://github.com/spring-cloud/spring-cloud-contract/releases
Stub(응답 지정)
WireMock 설명에서도 언급했었지만, Http 응답값을 미리 지정해두고 사용한다고 했었다.
이때 Stub 이란 http 요청 경로와 그에 대한 응답을 설정해서 제공하는것을 의미한다.
일반적으로 응답 File 을 미리 생성해두고 사용하며 Http 요청/응답 에 대한 상세 설정 및
단순 응답 데이터를 설정해서 사용할 수도 있다.
예제에서는 단순 JSON 데이터를 File 로 만들어서 사용했다.
{
"movieInfoId": "1",
"name": "Batman Begins",
"year": 2005,
"cast": [
"Christian Bale",
"Michael Cane"
],
"release_date": "2005-06-15"
}
해당 파일들의 경우 /test/resources/__files 가 기본 경로이며, 별도의 Directory 를 지정하여 사용할 수도 있다.
.willReturn
...
.withBody(getMockResponseBodyByPath("custom/file.json")
// 혹은
@AutoConfigureWireMock(stubs="classpath:/custom")
WireMock 을 적용한 전체 테스트코드
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("local")
@AutoConfigureWebTestClient
@AutoConfigureWireMock(port = 8084) // Spin up a WireMock server on port 8084
@TestPropertySource(
properties = {
"restClient.moviesInfoUrl=http://localhost:8084/api/movieInfos",
"restClient.reviewUrl=http://localhost:8084/api/reviews"
}
)
class MoviesControllerIntgTest {
@Autowired
WebTestClient webTestClient;
@Test
@DisplayName("Retrieve Movie By ID: 200 OK")
void retrieveMovieById() {
var movieId = "abc";
// MovieInfo: you can receive response, If you request this Specific Url
stubFor(get(urlEqualTo("/api/movieInfos/" + movieId))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBodyFile("movieinfo.json")));
// reviews: you use to QueryParam, But We can use Easy way to get response
// UrlPathEqualTo is, if path is same, you can get response -> not need to QueryParam
stubFor(get(urlPathEqualTo("/api/reviews"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBodyFile("reviews.json")));
webTestClient.get()
.uri("/api/movies/{id}", movieId)
.exchange()
.expectStatus().isOk()
.expectBody(Movie.class)
.consumeWith(movieEntityExchangeResult -> {
var result = movieEntityExchangeResult.getResponseBody();
assert Objects.requireNonNull(result).getReviewsList().size() == 2;
assertEquals("Batman Begins", result.getMovieInfo().getName());
});
}
}
우선 WireMock 을 사용하여 Mock Server 테스트를 진행하기 위해서는
@AutoConfigureWireMock 어노테이션을 사용해야 한다.
- Mock 서버를 Bean 으로 등록하고 사용하기 위함
@AutoConfigureWireMock(port = 8084) // Spin up a WireMock server on port 8084
@TestPropertySource(
properties = {
"restClient.moviesInfoUrl=http://localhost:8084/api/movieInfos",
"restClient.reviewUrl=http://localhost:8084/api/reviews"
}
)
Annotation 과 properties 를 보면 이해하기 쉬울것이다.
port = {포트 지정} 을 통해 특정포트를 지정할 수 있으며
default = 8080 설정이다.
Random 한 port 설정시 port = 0 으로 설정하면 된다.
아래의 @TestPropertySource 의 properties 는 요청을 보낼 Mock Server 의 주소인걸 알 수 있다.
유의 할점은 위의 예제처럼 port 를 지정한다면 port 를 매핑시켜 사용하면 되지만, 랜덤한 포트를 사용할 경우는
아래처럼 사용할 수 있다.
@AutoConfigureWireMock(port = 0)
@TestPropertySource(
properties = {
"restClient.moviesInfoUrl=http://localhost:${wiremock.server.port}/api/movieInfos",
"restClient.reviewUrl=http://localhost:${wiremock.server.port}/api/reviews"
}
)
WireMock Test Code 작성
stubFor(get(urlEqualTo("/api/movieInfos/" + movieId))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBodyFile("movieinfo.json")));
stubFor(get(urlPathEqualTo("/api/reviews"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBodyFile("reviews.json")));
stubFor() 이라는 static Method 를 사용하며 get,post,update,delete 와 같은 Http 요청을 Test 할 수 있다.
위처럼 withBodyFile() 를 통해 응답값 파일을 직접 지정할 수도 있으며, 아래처럼 특정 경로의 파일을 지정해줄수도 있다.
.withBody(getMockResponseBodyByPath("custom/file.json")
위의 2가지 예제를 본다면 Url Method 가 다른것을 볼 수 있는데
urlEqualTo: path, 쿼리 파라미터를 포함하여 URL 전체가 지정한 값과 정확히 일치하는 경우
urlMatching: URL 전체를 정규 표현식(Regex) 를 사용하여 매칭(/api/movieinfos.*)
urlPathEqualTo: path 만 일치시키며 Query Parameter 는 무시한다.
urlPathMatching: path 부분만 정규표현식으로 매칭하며, Query Parameter 를 무시한다(/api/.*)
이후 Test 를 진행하게 되면 WireMock 에서의 Response 가 Log 로 출력되게 된다.
간단하게 JSON 값만을 테스트 하는 방법들을 소개했었는데, 실제 Http Response 를 응답값으로 지정하여 사용하면
더욱 정교한 Test 를 진행 할 수 있을것 같다.
'spring & boot > Spring & Spring Boot' 카테고리의 다른 글
[Spring boot] Spring Boot 3.x^ - Swagger 적용(2) (0) | 2024.11.14 |
---|---|
[Spring boot] OAuth2: Authorization-Server (인증 서버 구축)(1) (1) | 2024.11.13 |
[Spring Boot]Jasypt 원리 및 사용방법(yml 설정 암복호화, Boot 3.x^) +String Util Class (0) | 2024.08.14 |
[Spring Security] x-frame-option 헤더 설정(iframe) (1) | 2024.04.03 |
[SpringBoot] QR코드 생성 & Test(링크이동) - zxing (2) | 2023.03.24 |
댓글