토리맘의 한글라이즈 프로젝트 logo 토리맘의 한글라이즈 프로젝트

스프링 클라우드 컨트랙트 공식 레퍼런스를 한글로 번역한 문서입니다.

전체 목차는 여기에 있습니다.


Spring Cloud Contract WireMock 모듈을 이용하면 스프링 부트 애플리케이션에서 WireMock을 사용할 수 있다. 자세한 내용은 Spring Cloud Contract 레포지토리에 있는 samples 폴더를 참고해라.

스프링 부트 애플리케이션에서 임베디드 톰캣을 사용하고 있다면 (spring-boot-starter-web의 기본 동작), 클래스패스에 spring-cloud-starter-contract-stub-runner를 추가하고 테스트 코드에 @AutoConfigureWireMock을 선언해라. 그러면 Wiremock이 스텁stub 서버로 실행되며, 테스트 코드 내에서 Java API를 사용하거나 미리 정의해둔 JSON을 이용해 스텁stub의 동작 방식을 등록할 수 있다.

스텁stub 서버를 다른 포트에서 시작하고 싶다면, (예를 들어) @AutoConfigureWireMock(port=9999)를 사용해라. 랜덤 포트를 사용하고 싶다면 0으로 설정하면 된다. 스텁stub 서버의 포트는 wiremock.server.port 프로퍼티를 통해 테스트 애플리케이션 컨텍스트에 바인딩된다. @AutoConfigureWireMock을 사용하면 테스트 애플리케이션 컨텍스트에 WiremockConfiguration 타입 빈이 추가되며, 이 빈은 동일한 컨텍스트를 가진 메소드와 클래스에 재사용한다. Spring 통합 테스트에서도 마찬가지다. 그 외에도, 테스트 코드에서 WireMockServer 타입 빈을 주입받을 수 있다. 등록된 WireMock 서버는 각 테스트 클래스를 실행한 뒤에 리셋된다. 각 테스트 메소드를 실행한 뒤에 매핑 정보를 리셋해야 한다면, wiremock.reset-mappings-after-each-test 프로퍼티를 true로 설정해라.

목차

3.7.1. Registering Stubs Automatically

@AutoConfigureWireMock을 사용하면 파일 시스템이나 클래스패스에 있는 (디폴트로는 file:src/test/resources/mappings에서) WireMock JSON 스텁stub을 등록한다. 이 위치는 어노테이션의 stubs 속성을 통해 커스텀할 수 있다. 여기에는 Ant 스타일의 리소스 패턴이나 디렉토리를 지정한다. 디렉토리를 지정하면 해당 디렉토리에 있는 ***/**.json을 등록한다. 다음은 그 예시를 보여준다:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWireMock(stubs="classpath:/stubs")
public class WiremockImportApplicationTests {

	@Autowired
	private Service service;

	@Test
	public void contextLoads() throws Exception {
		assertThat(this.service.go()).isEqualTo("Hello World!");
	}

}

사실 WireMock은 stubs 속성으로 커스텀한 위치뿐만 아니라, 항상 src/test/resources/mappings에 있는 매핑 정보도 함께 로드한다. 이 동작을 변경하려면 다음 섹션에서 설명하는 파일 루트 정보도 변경하면 된다.

참고로, stubs에 있는 매핑 정보는 Wiremock의 “디폴트 매핑”으로 간주하지 않으며, 테스트 중에 com.github.tomakehurst.wiremock.client.WireMock.resetToDefaultMappings를 호출해도 stubs에 위치한 매핑 정보는 포함되지 않는다. 반면, org.springframework.cloud.contract.wiremock.WireMockTestExecutionListener는 모든 테스트 클래스 실행을 마친 후에, 그리고 설정에 따라 (wiremock.reset-mappings-after-each-test 프로퍼티로 관리한다) 모든 테스트 메소드 실행을 마친 후에 매핑 정보를 리셋한다.

Spring Cloud Contract의 디폴트 스텁stub jar를 사용 중이라면, /META-INF/group-id/artifact-id/versions/mappings/ 폴더에 스텁stub을 저장한다. 임베딩한 모든 JAR 상에서 이곳에 위치한 스텁stub을 전부 등록하고 싶다면, 다음 구문을 사용하면 된다:

@AutoConfigureWireMock(port = 0, stubs = "classpath*:/META-INF...

3.7.2. Using Files to Specify the Stub Bodies

WireMock은 클래스패스나 파일 시스템에 있는 파일에서 응답 body를 읽어올 수 있다. 파일 시스템을 사용할 땐 JSON DSL 응답에 (말 그대로) body를 바로 정의하는 대신, bodyFileName을 정의한다. 이때 파일은 루트 디렉토리를 기준으로 리졸브한다 (디폴트는 src/test/resources/__files). 이 위치를 커스텀하려면 @AutoConfigureWireMock 어노테이션의 files 속성에 원하는 상위 디렉토리를 설정하면 된다 (즉, __files는 하위 디렉토리다). Spring 리소스 표기법을 사용해 file:… 또는 classpath:… 위치를 참조할 수도 있다. 일반 URL은 지원하지 않는다. 값은 여러 개 지정할 수 있으며, 이 경우 WireMock은 응답 body를 찾을 때 존재하는 첫 번째 파일을 리졸브한다.

files 루트를 설정하면 자동 로드하는 스텁stub에도 영향을 미친다. 스텁stub을 자동으로 로드할 땐 루트 경로 아래 mappings라는 하위 디렉토리에서 가져오기 때문이다.

files의 값을 변경해도 stubs 속성으로 명시한 곳에서 스텁stub을 로드하는 동작에는 영향을 미치지 않는다.

3.7.3. Alternative: Using JUnit Rules

좀더 일반적인 환경에선 JUnit @Rules를 사용해 WireMock 서버를 시작하고 중지할 수 있다. 이땐 다음 예시에서 처럼, WireMockSpring 클래스를 활용해 Options 인스턴스를 가져오면 된다:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class WiremockForDocsClassRuleTests {

    // Start WireMock on some dynamic port
    // for some reason `dynamicPort()` is not working properly
    public static WireMockServer wiremock = new WireMockServer(WireMockSpring.options().dynamicPort());

    @BeforeAll
    static void setupClass() {
        wiremock.start();
    }

    @AfterEach
    void after() {
        wiremock.resetAll();
    }

    @AfterAll
    static void clean() {
        wiremock.shutdown();
    }

    // A service that calls out over HTTP to wiremock's port
    @Autowired
    private Service service;

    @BeforeEach
    public void setup() {
        this.service.setBase("http://localhost:" + wiremock.port());
    }

    // Using the WireMock APIs in the normal way:
    @Test
    public void contextLoads() throws Exception {
        // Stubbing WireMock
        wiremock.stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
                .withHeader("Content-Type", "text/plain").withBody("Hello World!")));
        // We're asserting if WireMock responded properly
        assertThat(this.service.go()).isEqualTo("Hello World!");
    }

}

@ClassRule은 이 클래스에 있는 모든 메소드를 실행한 후에 서버를 종료한다는 뜻이다.

3.7.4. Relaxed SSL Validation for Rest Template

WireMock을 사용하면 https 프로토콜을 사용하는 서버에도 스텁stub을 적용할 수 있다. 애플리케이션 통합 테스트에서 https 서버의 스텁stub과 연결하다보면, SSL 인증서가 유효하지 않다는 것을 깨닫게 될 거다 (자체 설치 인증서와 관련된 일반적인 이슈). 클라이언트가 http를 사용하도록 설정할 수 있으면 가장 좋다. 하지만 이 방법이 불가능하다면, 스프링을 이용해 SSL 유효성 검사와 관련된 에러를 무시하는 HTTP 클라이언트를 구성하면 된다 (물론 테스트 환경에서만 이렇게 해야 한다).

가장 쉬운 방법은, 다음 예제와 같이 애플리케이션에서 스프링 부트의 RestTemplateBuilder를 사용하는 거다:

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
	return builder.build();
}

콜백을 통해 RestTemplateBuilder가 전달되기 때문에 직접 초기화할 수 있다. 여기에서 클라이언트에 SSL 유효성 검사 관련 설정을 추가하면 된다. 사실 @AutoConfigureWireMock 어노테이션이나 stub runner를 사용한다면 테스트에 자동으로 세팅될 거다. JUnit @Rule을 사용 중이라면, 다음 예제와 같이 @AutoConfigureHttpClient 어노테이션도 추가해야 한다:

@RunWith(SpringRunner.class)
@SpringBootTest("app.baseUrl=https://localhost:6443")
@AutoConfigureHttpClient
public class WiremockHttpsServerApplicationTests {

	@ClassRule
	public static WireMockClassRule wiremock = new WireMockClassRule(
			WireMockSpring.options().httpsPort(6443));
...
}

spring-boot-starter-test를 사용하는 경우, 클래스패스에 Apache HTTP 클라이언트가 있다면, RestTemplateBuilder에서 이를 확인해 SSL 관련 에러를 무시하도록 설정한다. 디폴트 java.net 클라이언트를 사용한다면 이 어노테이션이 필요하지 않다 (사용해도 상관 없다). 현재 다른 클라이언트에 대한 지원은 없지만 향후 릴리즈에 추가될 수 있다.

커스텀 RestTemplateBuilder를 비활성화하려면 wiremock.rest-template-ssl-enabled 프로퍼티를 false로 설정해라.

3.7.5. WireMock and Spring MVC Mocks

Spring Cloud Contract는 WireMock의 스텁stub JSON을 스프링 MockRestServiceServer로 로드할 수 있는 편의 클래스를 제공한다. 다음 예시를 참고해라:

@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class WiremockForDocsMockServerApplicationTests {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private Service service;

    @Test
    public void contextLoads() throws Exception {
        // will read stubs classpath
        MockRestServiceServer server = WireMockRestServiceServer.with(this.restTemplate)
                .baseUrl("https://example.org").stubs("classpath:/stubs/resource.json")
                .build();
        // We're asserting if WireMock responded properly
        assertThat(this.service.go()).isEqualTo("Hello World");
        server.verify();
    }

}

mock을 호출할 때마다 baseUrl 값이 추가되며, stubs() 메소드는 메소드 인자로 스텁stub 경로의 리소스 패턴을 받는다. 위 예제에서는 /stubs/resource.json에 정의되어 있는 스텁stub을 mock 서버에 로드한다. RestTemplateexample.org/에 방문하라는 요청을 받으면, 이 URL에 지정된 응답을 가져온다. 스텁stub 패턴은 여러 개 지정할 수 있으며, 디렉토리(하위 디렉토리까지 포함한 디렉토리 내 모든 .json 파일), 고정 파일명(위 예제에서 사용), Ant 스타일 패턴을 사용할 수 있다. JSON 포맷은 일반적인 WireMock 포맷으로, WireMock 웹사이트에서 확인할 수 있다.

현재 Spring Cloud Contract Verifier는 스프링 부트 임베디드 서버로 Tomcat, Jetty, Undertow를 지원하며, WireMock 자체는 Jetty의 특정 버전(현재 9.2)을 “네이티브”로 지원한다. 네이티브 Jetty를 사용하려면 네이티브 Wiremock 의존성을 추가하고 스프링 부트 컨테이너(사용 중이라면)를 제외시켜야 한다.


Next :
Build Tools
스프링 클라우드 컨트랙트의 테스트 빌드 툴

전체 목차는 여기에 있습니다.

<< >>

TOP