unit-test-wiremock-rest-api は何をしますか?
WireMockを使用して外部REST APIを単体テストするためのパターンを提供します。APIレスポンスのスタブ化、リクエスト詳細の検証、障害(タイムアウト、4xx/5xxエラー)のシミュレーション、および実際のネットワークコールなしでのHTTPクライアント動作の検証を行います。外部APIとのサービス統合をテストする際やHTTPエンドポイントをモックする際に使用します。
概要
このスキルは、WireMockを使って外部REST APIとの依存関係をモックし、単体テストを効果的に行うためのパターンを示します。WireMockを使用することで、以下のことが可能になります:
- APIレスポンスのスタブ化:外部APIの応答を定義し、テストで期待される動作をシミュレーションします。
- リクエスト検証:送信されたHTTPリクエスト(メソッド、URL、ヘッダー、ボディ)が期待通りであることを確認します。
- 障害シミュレーション:タイムアウトやエラーステータスコード(4xx、5xx)をテストし、アプリケーションの耐障害性を検証します。
- HTTPクライアントの検証:実際のネットワークコールなしに、HTTPクライアントの動作(リトライ、タイムアウト処理など)を検証します。
前提条件
- Java 11以降
- MavenまたはGradle
- WireMock(スタンドアロンモードまたはJUnit 5との統合)
インストール
Maven
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8-standalone</artifactId>
<version>2.35.0</version>
<scope>test</scope>
</dependency>
Gradle
testImplementation 'com.github.tomakehurst:wiremock-jre8-standalone:2.35.0'
基本的な使用例
1. WireMockサーバーのセットアップ
import com.github.tomakehurst.wiremock.WireMockServer;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
public class ApiTest {
private WireMockServer wireMockServer;
@BeforeEach
void setUp() {
wireMockServer = new WireMockServer(options().port(8089));
wireMockServer.start();
}
@AfterEach
void tearDown() {
wireMockServer.stop();
}
}
2. APIレスポンスのスタブ化
import static com.github.tomakehurst.wiremock.client.WireMock.*;
@Test
void testGetUser() {
// スタブの設定
stubFor(get(urlEqualTo("/api/users/1"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"id\":1,\"name\":\"John Doe\"}")));
// 実際のテストコード
String response = restTemplate.getForObject("http://localhost:8089/api/users/1", String.class);
assertEquals("{\"id\":1,\"name\":\"John Doe\"}", response);
}
3. リクエストの検証
@Test
void testPostUser() {
stubFor(post(urlEqualTo("/api/users"))
.willReturn(aResponse()
.withStatus(201)
.withHeader("Location", "/api/users/2")));
// リクエスト送信
restTemplate.postForEntity("http://localhost:8089/api/users", newUser, Void.class);
// リクエスト検証
verify(postRequestedFor(urlEqualTo("/api/users"))
.withRequestBody(matchingJsonPath("$.name", containing("Jane")))
.withHeader("Authorization", containing("Bearer")));
}
4. 障害シミュレーション
// タイムアウトのシミュレーション
stubFor(get(urlEqualTo("/api/slow"))
.willReturn(aResponse()
.withFixedDelay(5000))); // 5秒の遅延
// 4xxエラーのシミュレーション
stubFor(get(urlEqualTo("/api/not-found"))
.willReturn(aResponse()
.withStatus(404)
.withBody("Not Found")));
// 5xxエラーのシミュレーション
stubFor(get(urlEqualTo("/api/server-error"))
.willReturn(aResponse()
.withStatus(500)
.withBody("Internal Server Error")));
高度なパターン
動的レスポンス
import static com.github.tomakehurst.wiremock.client.WireMock.*;
stubFor(get(urlPathMatching("/api/users/([0-9]+)"))
.willReturn(aResponse()
.withTransformers("response-template")
.withBody("{\"id\":\"{{request.pathSegments.[2]}}\"}")));
リクエストマッチング
// ヘッダー一致
stubFor(get(urlEqualTo("/api/secure"))
.withHeader("Authorization", equalTo("Bearer token123"))
.willReturn(aResponse().withStatus(200)));
// ボディパターン一致
stubFor(post(urlEqualTo("/api/data"))
.withRequestBody(containing("\"key\":\"value\""))
.willReturn(aResponse().withStatus(200)));
複数スタブの管理
@Test
void testMultipleScenarios() {
// 正常系
stubFor(get(urlEqualTo("/api/success"))
.willReturn(aResponse().withStatus(200)));
// エラー系
stubFor(get(urlEqualTo("/api/failure"))
.willReturn(aResponse().withStatus(500)));
// リダイレクト
stubFor(get(urlEqualTo("/api/redirect"))
.willReturn(aResponse()
.withStatus(302)
.withHeader("Location", "/api/success")));
}
ベストプラクティス
- テストの独立を保つ: 各テストで独自のWireMockサーバーインスタンスを使用し、スタブ定義をクリーンアップします。
- レスポンスを現実的にする: 実際のAPIのレスポンス構造を模倣し、適切なHTTPステータスコードとヘッダーを使用します。
- 状態管理を避ける: 可能な限り、テスト間で状態を共有しないようにします。
- エラーケースを含める: ネットワーク障害、タイムアウト、予期しない応答など、エッジケースをテストします。
- リクエスト検証を活用する: アプリケーションが期待通りのリクエストを送信していることを確認します。
参考リンク
ライセンス
このプロジェクトはMITライセンスの下で提供されています。詳細はLICENSEファイルを参照してください。