WireMockの紹介

WireMockの概要

1. 概要

WireMockは、Webサービスをスタブおよびモックするためのライブラリです。 実際のWebサービスと同じように接続できるHTTPサーバーを構築します。

WireMockサーバーが動作しているときは、期待値を設定し、サービスを呼び出して、その動作を確認できます。

2. Mavenの依存関係

WireMockライブラリを利用できるようにするには、次の依存関係をPOMに含める必要があります。


    com.github.tomakehurst
    wiremock
    1.58
    test

3. プログラムで管理されるサーバー

このセクションでは、WireMockサーバーを手動で構成する方法について説明します。 i.e. JUnit自動構成のサポートなし。 使用法は、非常に単純なスタブによって示されます。

3.1. サーバーのセットアップ

WireMockサーバーは次のようにインスタンス化できます。

WireMockServer wireMockServer = new WireMockServer(String host, int port);

引数が指定されていない場合、サーバーホストはデフォルトでlocalhostになり、サーバーポートはデフォルトで8080になります。

サーバーは、次の2つの簡単な方法を使用して起動および停止できます。

wireMockServer.start();

And:

wireMockServer.stop();

3.2. 基本的な使い方

WireMockライブラリーは、基本的な使用法によって最初に示されます。ここでは、追加の構成なしで正確なURLのスタブが提供されます。 サーバーインスタンスを作成しましょう。

WireMockServer wireMockServer = new WireMockServer();

クライアントが接続する前にWireMockサーバーが実行されている必要があります。

wireMockServer.start();

次に、Webサービスがスタブ化されます。

configureFor("localhost", 8080);
stubFor(get(urlEqualTo("/example")).willReturn(aResponse().withBody("Welcome to example!")));

このチュートリアルでは、Apache HttpClient APIを使用して、サーバーに接続するクライアントを表します。

CloseableHttpClient httpClient = HttpClients.createDefault();

その後、それぞれリクエストが実行され、レスポンスが返されます。

HttpGet request = new HttpGet("http://localhost:8080/example");
HttpResponse httpResponse = httpClient.execute(request);

ヘルパーメソッドを使用して、httpResponse変数をStringに変換します。

String responseString = convertResponseToString(httpResponse);

その変換ヘルパーメソッドの実装は次のとおりです。

private String convertResponseToString(HttpResponse response) throws IOException {
    InputStream responseStream = response.getEntity().getContent();
    Scanner scanner = new Scanner(responseStream, "UTF-8");
    String responseString = scanner.useDelimiter("\\Z").next();
    scanner.close();
    return responseString;
}

次のコードは、サーバーが予期されるURLへのリクエストを取得し、クライアントに到着する応答が送信されたものとまったく同じであることを確認します。

verify(getRequestedFor(urlEqualTo("/example")));
assertEquals("Welcome to example!", stringResponse);

最後に、WireMockサーバーを停止してシステムリソースを解放する必要があります。

wireMockServer.stop();

4. JUnitマネージドサーバー

セクション3とは対照的に、このセクションでは、JUnitRuleを使用したWireMockサーバーの使用法を示します。

4.1. サーバーのセットアップ

WireMockサーバーは、@Ruleアノテーションを使用してJUnitテストケースに統合できます。 これにより、JUnitはライフサイクルを管理し、各テストメソッドの前にサーバーを起動し、メソッドが戻った後にサーバーを停止できます。

プログラムで管理されたサーバーと同様に、JUnitで管理されたWireMockサーバーは、指定されたポート番号を持つJavaオブジェクトとして作成できます。

@Rule
public WireMockRule wireMockRule = new WireMockRule(int port);

引数が指定されていない場合、サーバーポートはデフォルト値の8080を取ります。 サーバーホスト(デフォルトはlocalhost)、およびその他の構成は、Optionsインターフェースを使用して指定できます。

4.2. URLマッチング

WireMockRuleインスタンスを設定した後、次のステップはスタブを構成することです。 このサブセクションでは、正規表現を使用してサービスエンドポイントのRESTスタブを提供します。

stubFor(get(urlPathMatching("/example/.*"))
  .willReturn(aResponse()
  .withStatus(200)
  .withHeader("Content-Type", "application/json")
  .withBody("\"testing-library\": \"WireMock\"")));

HTTPクライアントの作成、リクエストの実行、レスポンスの受信に移りましょう。

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet request = new HttpGet("http://localhost:8080/example/wiremock");
HttpResponse httpResponse = httpClient.execute(request);
String stringResponse = convertHttpResponseToString(httpResponse);

上記のコードスニペットは、変換ヘルパーメソッドを利用しています。

private String convertHttpResponseToString(HttpResponse httpResponse) throws IOException {
    InputStream inputStream = httpResponse.getEntity().getContent();
    return convertInputStreamToString(inputStream);
}

これにより、別のプライベートメソッドが使用されます。

private String convertInputStreamToString(InputStream inputStream) {
    Scanner scanner = new Scanner(inputStream, "UTF-8");
    String string = scanner.useDelimiter("\\Z").next();
    scanner.close();
    return string;
}

スタブの動作は、以下のテストコードによって検証されます。

verify(getRequestedFor(urlEqualTo("/example/wiremock")));
assertEquals(200, httpResponse.getStatusLine().getStatusCode());
assertEquals("application/json", httpResponse.getFirstHeader("Content-Type").getValue());
assertEquals("\"testing-library\": \"WireMock\"", stringResponse);

4.3. ヘッダーマッチングのリクエスト

次に、ヘッダーのマッチングを使用してREST APIをスタブ化する方法を示します。 スタブ構成から始めましょう:

stubFor(get(urlPathEqualTo("/example/wiremock"))
  .withHeader("Accept", matching("text/.*"))
  .willReturn(aResponse()
  .withStatus(503)
  .withHeader("Content-Type", "text/html")
  .withBody("!!! Service Unavailable !!!")));

前のサブセクションと同様に、同じヘルパーメソッドを使用して、HttpClient APIを使用したHTTP相互作用を示します。

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet request = new HttpGet("http://localhost:8080/example/wiremock");
request.addHeader("Accept", "text/html");
HttpResponse httpResponse = httpClient.execute(request);
String stringResponse = convertHttpResponseToString(httpResponse);

次の検証とアサーションにより、前に作成したスタブの機能が確認されます。

verify(getRequestedFor(urlEqualTo("/example/wiremock")));
assertEquals(503, httpResponse.getStatusLine().getStatusCode());
assertEquals("text/html", httpResponse.getFirstHeader("Content-Type").getValue());
assertEquals("!!! Service Unavailable !!!", stringResponse);

4.4. ボディマッチングをリクエストする

WireMockライブラリを使用して、REST APIをボディマッチングでスタブ化することもできます。 この種のスタブの構成は次のとおりです。

stubFor(post(urlEqualTo("/example/wiremock"))
  .withHeader("Content-Type", equalTo("application/json"))
  .withRequestBody(containing("\"testing-library\": \"WireMock\""))
  .withRequestBody(containing("\"creator\": \"Tom Akehurst\""))
  .withRequestBody(containing("\"website\": \"wiremock.org\""))
  .willReturn(aResponse()
  .withStatus(200)));

次に、リクエストの本文として使用されるStringEntityオブジェクトを作成します。

InputStream jsonInputStream
  = this.getClass().getClassLoader().getResourceAsStream("wiremock_intro.json");
String jsonString = convertInputStreamToString(jsonInputStream);
StringEntity entity = new StringEntity(jsonString);

上記のコードは、前に定義した変換ヘルパーメソッドの1つであるconvertInputStreamToStringを使用しています。

クラスパス上のwiremock_intro.jsonファイルの内容は次のとおりです。

{
    "testing-library": "WireMock",
    "creator": "Tom Akehurst",
    "website": "wiremock.org"
}

HTTP要求および応答は、次のように構成および実行できます。

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost request = new HttpPost("http://localhost:8080/example/wiremock");
request.addHeader("Content-Type", "application/json");
request.setEntity(entity);
HttpResponse response = httpClient.execute(request);

これは、スタブの検証に使用されるテストコードです。

verify(postRequestedFor(urlEqualTo("/example/wiremock"))
  .withHeader("Content-Type", equalTo("application/json")));
assertEquals(200, response.getStatusLine().getStatusCode());

4.5. スタブの優先順位

前のサブセクションでは、HTTP要求が1つのスタブのみに一致する状況を扱いました。 要求に一致する以上のものがある場合は、より複雑になります。 デフォルトでは、このような場合、最後に追加されたスタブが優先されます。 ただし、ユーザーはその動作をカスタマイズして、WireMockスタブをさらに制御することができます。

優先レベルを設定する場合と設定しない場合の両方で、着信リクエストが同時に2つのスタブに一致する場合のWireMockサーバーの動作を示します。 両方のシナリオで、次のプライベートヘルパーメソッドが使用されます。

private HttpResponse generateClientAndReceiveResponseForPriorityTests() throws IOException {
    CloseableHttpClient httpClient = HttpClients.createDefault();
    HttpGet request = new HttpGet("http://localhost:8080/example/wiremock");
    request.addHeader("Accept", "text/xml");
    return httpClient.execute(request);
}

まず、優先度レベルを考慮せずに2つのスタブを構成します。

stubFor(get(urlPathMatching("/example/.*"))
  .willReturn(aResponse()
  .withStatus(200)));
stubFor(get(urlPathEqualTo("/example/wiremock"))
  .withHeader("Accept", matching("text/.*"))
  .willReturn(aResponse()
  .withStatus(503)));

次に、HTTPクライアントを作成し、上記のヘルパーメソッドを使用してリクエストを実行します。

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

次のコードスニペットは、要求が両方に一致する場合、前に定義されたスタブに関係なく、最後に構成されたスタブが適用されることを確認します。

verify(getRequestedFor(urlEqualTo("/example/wiremock")));
assertEquals(503, httpResponse.getStatusLine().getStatusCode());

優先度レベルが設定されているスタブに移りましょう。数値が小さいほど優先度が高くなります。

stubFor(get(urlPathMatching("/example/.*"))
  .atPriority(1)
  .willReturn(aResponse()
  .withStatus(200)));
stubFor(get(urlPathEqualTo("/example/wiremock"))
  .atPriority(2)
  .withHeader("Accept", matching("text/.*"))
  .willReturn(aResponse()
  .withStatus(503)));

HTTPリクエストの作成と実行:

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

次のコードは、最後ではなく最初に構成されたスタブが適用される優先度レベルの効果を検証します。

verify(getRequestedFor(urlEqualTo("/example/wiremock")));
assertEquals(200, httpResponse.getStatusLine().getStatusCode());

5. 結論

このチュートリアルでは、WireMockを紹介し、URL、リクエストヘッダー、本文の照合など、さまざまな手法を使用してREST APIのテスト用にこのライブラリを設定および構成する方法を紹介しました。

すべての例とコードスニペットの実装は、a GitHub projectにあります。