WireMock
依存関係を追加する
build.gradleに以下の記述を追加する。
dependencies {
testImplementation 'com.github.tomakehurst:wiremock-jre8:2.31.0'
}
Junit5で使用する
Junit5でWireMockを使用する方法として、declarative
とprogrammatic
の2つの操作モードをサポートしている。
- declarative:アノテーションで簡単に設定が可能だが、限られた設定しかできないモード
- programmatic:設定をプログラムで記述する、様々な設定が可能なモード。
declarativeモードでの使用方法
テストクラスにアノテーション@WireMockTest
を付与する。
これにより、テスト実行時に単一のWireMockサーバーが起動する。デフォルトでランダムポート、HTTPとなる。
@WireMockTest class SampleTest { }
実行中のポート番号、ベースURLなどを取得するには、WireMockRuntimeInfo
をテストメソッドの引数に追加する。
@WireMockTest class SampleTest { @Test void test(WireMockRuntimeInfo info) { String baseUrl = info.getHttpsBaseUrl(); int port = info.getHttpPort(); } }
上記の例では、WireMock サーバは、最初のテストメソッドの前に起動し、最後のテストメソッドが終了した後に停止される。
スタブのマッピングやリクエストは、各テストメソッドの前にリセットされる。
ポート番号を固定化する場合は、アノテーションの属性にhttpPort
を追加する。
@WireMockTest(httpPort = 8080) class SampleTest { }
HTTPSを有効にするには、アノテーションの属性にhttpsEnabled
を追加する。この場合ランダムポートとなる。
@WireMockTest(httpsEnabled = true) class SampleTest { }
HTTPSでかつポートを固定する場合はアノテーションの属性にhttpPort
とhttpsEnabled
を両方追加する。
@WireMockTest(httpsEnabled = true, httpsPort = 8080) class SampleTest { }
programmaticモードでの使用方法
@RegisterExtension
アノテーションを付与したWireMockExtensionを複数定義することで、任意の数のWireMockインスタンスを実行することができ、設定をコントロールすることができる。
実行中のポート番号、ベースURLなどを取得するには、getRuntimeInfo()
メソッドを実行する。
class SampleTest { @RegisterExtension static WireMockExtension wm1 = WireMockExtension.newInstance() .options(WireMockConfiguration.wireMockConfig().httpDisabled(false).port(3000)) .build(); @RegisterExtension static WireMockExtension wm2 = WireMockExtension.newInstance() .options(WireMockConfiguration.wireMockConfig() .dynamicPort() .extensions(new ResponseTemplateTransformer(true))) .build(); @Test void test() { WireMockRuntimeInfo wm1RuntimeInfo = wm1.getRuntimeInfo(); int httpsPort = wm1RuntimeInfo.getHttpsPort(); } }
上記のように、WireMockExtensionをstaticで定義した場合、各WireMockサーバは最初のテストメソッドの前に起動し、最後のテストメソッドが終了した後に停止され、各テストメソッドの前にresetが呼び出される(declarativeモードと同じ)。
WireMockExtension定義時にstatic
を付与しない場合は各テストメソッドの前に生成・起動し、テストメソッドの終了後に停止する。
スタブを定義する
特定のHTTPメソッド、URI、ヘッダーの時のふるまいを定義することができる。
スタブを記述する際には、com.github.tomakehurst.wiremock.client.WireMock
クラスのメソッドを使用する。
以下の説明で使用するメソッドは、用意されているメソッドの一部なので詳しくは公式のページのDoc参照のこと。
スタブの定義をする際にはstubFor(MappingBuilder)
というメソッドを使用する。
stubFor(MappingBuilder)
の引数の中で具体的なリクエストやレスポンスを定義していく。
以下に具体的なスタブの定義方法を説明する。
1. HTTPメソッドを指定する
WireMockがサポートしているHTTPメソッドは get、post、put、delete、head、trace、optionsの7つ。
任意のHTTPメソッドで良い場合は、any
を指定する。
mock.stubFor(WireMock.get());
2. HTTPメソッドの引数にURLを指定する。
この時、urlEqualTo(String)
を使用した場合は、引数のURLと完全一致した際にこのスタブが実行される。
この他にuriMatching(String)
というメソッドもあり、このメソッドでは引数に正規表現を与え、マッチするURLの場合にこのスタブが実行される。
任意のURLで良い場合はanyURL()
を指定する。
mock.stubFor(WireMock.get(WireMock.urlEqualTo("/some/thing"))));
3. ヘッダー等を追加する
HTTPメソッドに続けてwith*()
メソッドを使用することで、リクエストにヘッダーなどを追加することができる。
URLの場合と同様に、期待値を指定する際に完全一致(equalTo())や正規表現(matching())、部分一致(containing())など様々なマッチングを使用することができる。詳しい説明はここを参照。
mock.stubFor(WireMock.get(WireMock.urlEqualTo("/some/thing")) .withQueryParam("name", WireMock.containing("Bob")) .withHeader("Content-Type", WireMock.equalTo("application/json")));
4. レスポンスの設定をする
リクエストの設定に続けてwillReturn()
メソッドを使用することで、レスポンスの定義をすることができる。
mock.stubFor(WireMock.get(WireMock.urlEqualTo("/some/thing")) .withQueryParam("name", WireMock.containing("Bob")) .withHeader("Content-Type", WireMock.equalTo("application/json")) .willReturn(WireMock.aResponse() .withHeader("Content-Type", "text/plain") .withBody("Hello world!")));
以下にスタブの定義の記述例を示す。
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; class SampleTest { @RegisterExtension static WireMockExtension mock = WireMockExtension.newInstance() .options(WireMockConfiguration.wireMockConfig().httpDisabled(false).port(3000)) .build(); @Test void test() { mock.stubFor(WireMock.get(WireMock.urlEqualTo("/some/thing?name=Bob")) .withQueryParam("name", WireMock.containing("Bob")) .withHeader("Content-Type", WireMock.equalTo("application/json")) .willReturn(WireMock.aResponse() .withHeader("Content-Type", "application/json") .withBody("Hello world!"))); } }
マッピングファイルを用意してスタブを登録することもできる。
src/test/resources/mappings
フォルダを作成し、その配下にjsonファイルを作成し、スタブの定義を記述する。
マッピングファイルででのスタブ定義例は以下の通り。
{ "request": { "method": "GET", "url": "/some/thing?name=Bob", "headers" : { "Content-Type": { "equalTo" : "application/json" } }, "queryParameters" : { "name" : { "containing" : "Bob" } } }, "response": { "status": 200, "body": "Hello world!", "headers": { "Content-Type": "application/json" } } }
スタブの優先度を設定する
複数のスタブを宣言している場合、あるURLを指定した時にマッチングするスタブが複数存在する場合がある。
デフォルトでは、より新しい(より後で定義した)スタブが優先される。
自分で優先度の設定を行いたい場合は、.atPriority(int)
メソッドを使用して優先度を設定する。
class SampleTest { @RegisterExtension static WireMockExtension mock = WireMockExtension.newInstance() .options(WireMockConfiguration.wireMockConfig().httpDisabled(false).port(3000)) .build(); @Test void test() { mock.stubFor(WireMock.get(WireMock.urlEqualTo("/api/some/thing")) // ↓ 追加 .atPriority(1) // ↑ 追加 .willReturn(WireMock.aResponse() .withHeader("Content-Type", "text/plain") .withBody("Hello world!"))); mock.stubFor(WireMock.get("/api/.*") // ↓ 追加 .atPriority(5) // ↑ 追加 .willReturn(WireMock.ok())); } }
優先度の設定はマッピングファイルでも設定可能である。
{ // ↓ 追加 "priority": 1, // ↑ 追加 "request": { "method": "GET", "url": "/some/thing" }, "response": { "status": 200, "body": "Hello world!", "headers": { "Content-Type": "text/plain" } } }
レスポンスボディの内容を外部ファイル化する
レスポンスボディの内容をテストコードやマッピングファイル内に直接記述したくない場合、別のファイルに記述してそれを読み込むようにすることができる。
レスポンスボディの内容を記述したファイルはsrc/test/resources/__files
配下に置く。
以下に設定例を示す。
以下のようなレスポンスボディを定義したファイルを準備する。
{ "message": "Hello World" }
テストコードにスタブを定義する場合は以下のように記述する。
.withBodyFile()
の引数にはsrc/test/resources/__files
以降のパスを記述する。
class SampleTest { @RegisterExtension static WireMockExtension mock = WireMockExtension.newInstance() .options(WireMockConfiguration.wireMockConfig().httpDisabled(false).port(3000)) .build(); @Test void test() { mock.stubFor(WireMock.get(WireMock.urlEqualTo("/some/thing")) .willReturn(WireMock.aResponse() .withHeader("Content-Type", "text/plain") // ↓ レスポンスボディを定義したファイルのパスを指定する .withBodyFile("clientTest/get001.json"))); } }
マッピングファイルに定義する場合は以下のように記述する。
src/test/resources/__files
以降のパスを指定する。
{ "request": { "method": "GET", "url": "/some/thing" }, "response": { "status": 200, // ↓ レスポンスボディを定義したファイルのパスを指定する "bodyFileName": "clientTest/get001.json", "headers": { "Content-Type": "text/plain" } } }
リクエストを検証する
verify()
メソッドを使用することで、実際に送信されたリクエストについて検証が可能である。
メソッドの第一引数に数値を渡すことで、そのリクエストの実行回数を検証することもできる。
詳しくはここを参照。
class SampleTest { @RegisterExtension static WireMockExtension wm1 = WireMockExtension.newInstance() .options(WireMockConfiguration.wireMockConfig().httpDisabled(false).port(3000)) .build(); @Test void test() { // verify前に実際にリクエストを送る処理を記述する wm1.verify(2, WireMock.getRequestedFor(WireMock.urlEqualTo("/api/test/query/headers?name=Bob")) .withQueryParam("name", WireMock.equalTo("Bob")) .withHeader("Content-Type", WireMock.equalTo("application/json")) .withHeader("Authorization", WireMock.equalTo("Bearer apiKey"))); } }
リクエストするたびにレスポンスを変更する
テストコードでもマッピングファイルでもどちらでも設定可能。
3回目までのレスポンスを定義してあるスタブに4回以上リクエストを送信した場合、
4回目以降は3回目に定義したレスポンスが返される。
- リクエストするたびにレスポンスを変更させたいスタブに
inScenario(String)
を設定する。
引数にはシナリオ名を指定する。 - スタブが実行されるのに必要な
whenScenarioStateIs(String)
を設定する。
最初のリクエストで実行したいスタブにはScenario.STARTED
を設定する。
2番目以降に実行したいスタブには任意の値を設定する。 - そのスタブが実行された後に実行したいスタブを指定する。
willSetStateTo(String)
の引数に次に実行したいスタブのwhenScenarioStateIs(String)
で設定した値を指定する。
以下にテストコードで設定した例を示す。
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; class SampleTest { @RegisterExtension static WireMockExtension mock = WireMockExtension.newInstance() .options(WireMockConfiguration.wireMockConfig().httpDisabled(false).port(3000)) .build(); @Test void test() { mock.stubFor(WireMock.get(WireMock.urlEqualTo("/api/test/retry")) .inScenario("retry") .whenScenarioStateIs(Scenario.STARTED) .willSetStateTo("second") .willReturn(WireMock.aResponse() .withStatus(500) .withHeader("Content-Type", "application/json") .withBody("{\"message\": \"Get Request Failed\"}"))); mock.stubFor(WireMock.get(WireMock.urlEqualTo("/api/test/retry")) .inScenario("retry") .whenScenarioStateIs("second") .willSetStateTo("third") .willReturn(WireMock.aResponse() .withStatus(500) .withHeader("Content-Type", "application/json") .withBody("{\"message\": \"Get Request Failed\"}"))); mock.stubFor(WireMock.get(WireMock.urlEqualTo("/api/test/retry")) .inScenario("retry") .whenScenarioStateIs("third") .willReturn(WireMock.aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("{\"message\": \"Get Request Success\"}"))); } }
上の設定をマッピングファイルで定義した場合、以下のようになる。
- リクエストするたびにレスポンスを変更するのに使用するスタブに
scenarioName
を設定する。 - スタブが実行されるのに必要な
requiredScenarioState
を設定する。
最初のリクエストで実行したいスタブにはStarted
を設定する。2番目以降に実行したいスタブには任意の値を設定する。 - そのスタブが実行された後に実行したいスタブを指定する。
newScenarioState
に次に実行したいスタブのrequiredScenarioState
を指定する。
{ "mappings": [ { "scenarioName": "retry", "requiredScenarioState": "Started", "newScenarioState": "second", "request": { "method": "GET", "url": "/api/test/retry" }, "response": { "status": 500, "body": "{\"message\": \"Get Request Failed\"}", "headers": { "Content-Type": "application/json" } } }, { "scenarioName": "retry", "requiredScenarioState": "second", "newScenarioState": "third", "request": { "method": "GET", "url": "/api/test/retry" }, "response": { "status": 500, "body": "{\"message\": \"Get Request Failed\"}", "headers": { "Content-Type": "application/json" } } }, { "scenarioName": "retry", "requiredScenarioState": "third", "request": { "method": "GET", "url": "/api/test/retry" }, "response": { "status": 200, "body": "{\"message\": \"Get Request Success\"}", "headers": { "Content-Type": "application/json" } } } ] }
Wiremock-standalone版を使用する
上記はWiremockをJunitで使用する方法を説明したが、Wiremockにはスタンドアロン版も存在している。
このページからjarファイルをダウンロードし、 jarファイルが存在するディレクトリで以下のコマンドを実行することで、モックサーバを起動することが出来る。
java -jar wiremock-jre8-standalone-2.33.2.jar
スタブの定義は、jarファイルと同じ階層にmappings
フォルダと__files
フォルダを作成し、そこにスタブの定義ファイルを格納する。
スタブの定義方法はJunit版と同じである。
Junit5の使用方法も記事にしているのでよかったらご覧ください。 olafnosuke.hatenablog.com