Spring BootのWebClientのテストにSpring Cloud ContractのWireMockを使う
Spring BootのWebClientのテストにSpring Cloud ContractのWireMockを使ってみたときのメモ。
(ちなみに、RestTemplateのテストで使えるMockRestServiceServerはWebClientをサポートしていなかった)
ソースコード
build.gradle
plugins { id 'org.springframework.boot' version '2.4.5' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = JavaVersion.VERSION_11 repositories { mavenCentral() } dependencyManagement { imports { mavenBom 'org.springframework.cloud:spring-cloud-dependencies:2020.0.2' } } dependencies { annotationProcessor 'org.projectlombok:lombok' compileOnly 'org.projectlombok:lombok' implementation 'org.springframework.boot:spring-boot-starter-webflux' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-stub-runner' testImplementation 'io.projectreactor:reactor-test' } test { useJUnitPlatform() }
テスト対象コード
GitHubApiClient.java
@Component public class GitHubApiClient { private final WebClient webClient; public GitHubApiClient(WebClient.Builder builder, @Value("${base-url:https://api.github.com}") String baseUrl) { webClient = builder.baseUrl(baseUrl).build(); } public Mono<User> getUser(String username) { return webClient.get() .uri("/users/{username}", username) .retrieve() .bodyToMono(User.class); } }
User.java
@JsonIgnoreProperties(ignoreUnknown = true) @JsonNaming(SnakeCaseStrategy.class) @Data public class User { private long id; private String login; ... }
テストコード
GitHubApiClientTest.java
package com.example; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @SpringBootTest(properties = "base-url=http://localhost:${wiremock.server.port}") @AutoConfigureWireMock(port = 0) class GitHubApiClientTest { @Autowired private GitHubApiClient client; @Test void getUser() { ResponseDefinitionBuilder response = aResponse().withBody("{\"login\":\"hirakida\",\"id\":100}") .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .withStatus(HttpStatus.OK.value()); stubFor(get(urlEqualTo("/users/hirakida")).willReturn(response)); Mono<User> result = client.getUser("hirakida"); StepVerifier.create(result) .expectNextMatches(user -> user != null && "hirakida".equals(user.getLogin()) && user.getId() == 100) .verifyComplete(); } }
参考
- https://docs.spring.io/spring-framework/docs/5.3.x/reference/html/web-reactive.html#webflux-client-testing
- https://github.com/spring-projects/spring-framework/issues/19852
- https://github.com/spring-projects/spring-boot/issues/8404
- https://youtu.be/wAYt4Z4SF7g?t=2310
- https://stackoverflow.com/questions/48707625/set-property-with-wiremock-random-port-in-spring-boot-test