본문 바로가기

spring

[WireMock] WireMock을 이용하여 Mock API 서버 사용하기

들어가면서

외부 API를 사용하게 된다면 어떤 응답이 올지? API 사용 시 장애 상황에 어떻게 서버가 동작하는지에 대한 대안을 테스트하기 위해서 모킹을 하고 싶은데 그 방법을 몰라서 이것저것 검색을 해보았다. 그중에서 WireMock을 이용한 모킹 처리에 대해서 알아보자.

WireMock 이란?

http 기반의 API 서비스를 모킹하기 위해서 사용하는 테스트 라이브러리다. 목 서버를 만드는 방식은 JAR파일을 통해서 가동하는 방식과 wiremock 이미지를 통해서 컨테이너를 띄우는 방식이 있다.

Test code 작성하기!

standalone 방식

우선 컨테이너 기반으로 목서버를 사용하지 않고 standalone으로 사용하려면 다음 의존성이 필요하다.
버전은 공식문서를 참고해서 필요한 버전을 명시하면 좋다.

testImplementation("org.wiremock:wiremock-standalone:3.2.0")

외부 API와 통신하기위한 클라이언트는 자유롭게 선택하면 되지만 나는 스프링의 RestClient을 이용하였다.
로컬에 목 서버를 가동하였을 때, GET http://localhost:{port}/으로 요청이 들어오면 목킹한 응답을 제공하는 테스트를 작성해 보자!

public class WireMockRestClientTest {

    @RegisterExtension
    static WireMockExtension wireMock = WireMockExtension.newInstance()
        .options(
            wireMockConfig()
                .dynamicPort()
        )
        .build();

    @DisplayName("mocking api server")
    @Test
    void test() {
        // given
        RestClient restClient = RestClient.builder()
            .baseUrl("http://localhost:" + wireMock.getPort() + "/")
            .build();

        String mockResponseBody = """
            {
                "mock": "hello world"
            }
            """;

        wireMock.stubFor(WireMock.get("/")
            .willReturn(aResponse()
                .withBody(mockResponseBody))
        );

        // when
        String body = restClient.get()
            .retrieve()
            .body(String.class);

        // then
        System.out.println("response = " + body);
        then(body).isEqualTo(mockResponseBody);
    }
}

WireMockExtention이 목 서버에 대한 스펙을 정의하는 클래스이다. 다른 방식을 wiremock에서 제공하지만 나는 이 방식을 사용했다.
테스트 코드에서는 클라이언트 객체(RestClient)를 생성하고 요청할 API 서버에 대한 스펙을 정의해 주었다. 그리고 실제 요청을 했을 때, 정의한 스펙에 맞는 response 값이 출력되는지 확인하고 검증하는 코드까지 작성한 테스트 코드이다.

하지만 목 서버가 중간에 장애로 인해서 가동이 중단된 상황을 어떻게 테스트로 작성하고 싶었는데 이 방식은 힘들어 보였다.
wiremock에서 컨테이너 기반의 목 서버를 제공하는 것을 알게 되어서 장애로 인한 가동이 중단된 상황을 테스트 코드로 작성하는 방법을 적용해 보았다.

test container 방식

test container는 외부 API 서버뿐만 아니라 데이터베이스, 캐시 서버, 이벤트 프로파이더 서버 등과 같은 외부 서버들에 대한 모킹을 컨테이너 제공하는 라이브러리이다. 도커만 설치되어 있다면 실제 가동되는 서버로 즉, 실제 서비스하고 있는 외부 시스템과 동일한 스펙의 서버로 테스트를 실행할 수 있는 장점이 있는 라이브러리이다. wiremock에서는 이 test container와 연동하여 컨테이너 기반의 목 서버를 가동할 수 있도록 제공한다.

사용을 위해서는 다음 의존성이 필요하다.

testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation("com.github.wiremock:wiremock-testcontainers-java:1.0-alpha-7")

스프링 부트 기반의 서버를 개발하기 때문에 스프링 부트 스타터 의존성을 추가했고 junit에 test contaienr를 사용하기 위한 의존성을 추가했다. 마지막으로 test container기반으로 wiremock을 사용하기 위한 의존성을 추가했다.

@Testcontainers(disabledWithoutDocker = true)
class WireMockTestContainerRestClientTest {

    @Container
    static WireMockContainer wiremockServer = new WireMockContainer("wiremock/wiremock:2.35.0")
        .withMappingFromResource("mocks-config.json")
        .withFileFromResource("album-photos-response.json");

    @Test
    void shouldGetAlbumById() {
        Long albumId = 1L;
        RestClient restClient = RestClient.builder()
            .baseUrl(
                "http://localhost:" + wiremockServer.getPort() + "/albums/" + albumId + "/photos")
            .build();

        ResponseEntity<String> response = restClient.get()
            .header("Content-Type", "application/json")
            .retrieve()
            .toEntity(String.class);

        System.out.println(response);
    }
}

test contaienr를 도커 환경에서 실행하기 위해서 @TestContainer(disabledWithOutDocker=true)를 사용했고 wiremock이미지에 대한 정보와 모킹한 api 스펙에 대한 정보를 매핑해 주었다. 관련 파일은 /src/test/resources 디렉터리에 저장해 주면 된다. 이후에는 처음 standalone과 같이 테스트 케이스를 작성하면 그 결과를 볼 수 있다. 프로젝트에 대한 구조는 github를 참고하면 이해가 좋을 것 같다.


테스트 컨테이너를 사용하면 컨테이너를 stop, start 메서드를 통해서 테스트 코드 내에서 제어할 수 있다. 만약 서버가 다운된 상황에서 장애 대응을 테스트하고 싶다면 test container를 통한 목 서버를 구동하는 솔루션이 적합하다고 생각한다.


더 자세한 사용법과 정보들은 test container 사이트와 wiremock 사이트를 찾아보고 공부를 해봐야겠다.

마주했던 에러

colima 사용 시 docker.sock 매핑 에러

회사에서는 도커 데스크톱의 설치가 라이센스 문제 때문에 불가했다. 그래서 다른 솔루션을 사용했는데 Colima라는 프로젝트였다. 하지만 도커 socket를 매핑하는 과정에서 문제가 발생했기에 test container가 실행이 되지 않았다. 해결 방법은 도커 소켓을 매핑시켜 주기 위해서 환경변수를 선언하는 것이었다.

export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock
export DOCKER_HOST="unix://${HOME}/.colima/docker.sock"

 

 

Testcontainers With Colima

Testcontainers is a Java library to support testing database operations with Docker containers. The library needs a working Docker context to spin up the containers. If you use Colima/Lima as a Docker replacement, you might come across the following error:

www.rockyourcode.com

Reference

연습 테스트 레포 링크
https://www.baeldung.com/spring-boot-built-in-testcontainers

 

JUnit 5+ Jupiter

WireMock includes a JUnit Jupiter extension which is used to manage the lifecycle and configuration of one or more WireMock instances in your test case.

wiremock.org

 

Quick Start: API Mocking with Java and JUnit 4

Shows how to write your API Client first test with WireMock and JUnit

wiremock.org

 

Testing REST API integrations in Micronaut applications using WireMock

This guide will explain how to test REST API integrations in a Micronaut application using Testcontainers WireMock module.

testcontainers.com