WireMockをTestContainersで立ち上げてJUnitテストを実行する方法
JUnit5でWireMockを起動する際には、@WireMockTestをクラスにつけて実行することが出来ますが、起動には時間がかかるためテスト起動時にコンテナを利用して1回だけ起動して後はそれを使い回すという方法になります。
今回コンテナの起動には TestContainers を利用します。
build.gradle
今回の記事で利用しているライブラリとバージョンです。
SpringBootは2.7.1を利用しています。
implementation 'org.springframework.boot:spring-boot-starter-webflux'
testImplementation 'com.github.tomakehurst:wiremock-jre8:2.33.2'
testImplementation 'org.testcontainers:testcontainers:1.17.3'
共通クラス
コンテナをシングルトンで起動するため、共通クラスを用意します。
GenericContainerでWireMockのコンテナイメージを起動します。staticで生成することでシングルトンとして扱われ、テストクラス実行の都度コンテナを立ち上げないように設定出来ます。参考: TestContainers -> Singleton containers
また起動したコンテナイメージへの接続portをWireMockインスタンスの生成に利用し、そのインスタンスを WireMock.configureFor
に指定することで、テストクラスで stubFor
などのスタブ生成で作成されたイメージへその設定が反映されるようになります。
application.ymlなどでAPI通信先の設定がしてある場合には、@DynamicPropertySource
を利用して接続先を切り替えておきます。
abstract class AbstractIntegrationTest {
private static final int WIREMOCK_ORIGINAL_PORT = 8080;
static final GenericContainer<?> wireMockImage =
new GenericContainer<>(DockerImageName.parse("wiremock/wiremock:2.33.2"))
.withExposedPorts(WIREMOCK_ORIGINAL_PORT);
static WireMock wireMock;
static {
wireMockImage.start();
wireMock = new WireMock(wireMockImage.getMappedPort(WIREMOCK_ORIGINAL_PORT));
WireMock.configureFor(wireMock);
}
// ここでapplication.ymlの設定値を変えてAPIの通信先をWireMockに切り替える
@DynamicPropertySource
static void registerProperties(DynamicPropertyRegistry registry) {
registry.add(
"api.hello-server.url",
() ->
"http://"
+ wireMockImage.getHost()
+ ":"
+ wireMockImage.getMappedPort(WIREMOCK_ORIGINAL_PORT));
}
}
テストクラス
テストクラスでは、共通クラスを継承してあげればあとはWireMockの通常の使い方(stubForの利用)が可能です。
WireMockのMappingsが残って影響があるようなケースもあるため、BeforeEachで wireMock.removeMappings();
を呼び出してあげれば都度都度Mappingsが削除することが可能です。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloIntegrationTest extends AbstractIntegrationTest {
@Autowired private WebTestClient webTestClient;
@BeforeEach
void setup() {
wireMock.removeMappings();
}
@Test
void Taroを返す() {
stubFor(get("/hello/").willReturn(okJson(" { \"name\": \"taro\", \"age\": 20 }")));
webTestClient
.get()
.uri("/hello")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.name")
.isEqualTo("taro");
}
@Test
void Hanakoを返す() {
stubFor(get("/hello/").willReturn(okJson(" { \"name\": \"hanako\", \"age\": 22 }")));
webTestClient
.get()
.uri("/hello")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.name")
.isEqualTo("hanako");
}
}
Discussion