ドキュメント

§Webサービスクライアントのテスト

Webサービスクライアントの作成には、リクエストの準備、ボディのシリアライズとデシリアライズ、正しいヘッダーの設定など、多くのコードが必要になります。これらのコードの多くは文字列や型付けの弱いマップを扱うため、テストが非常に重要です。しかし、テストにはいくつかの課題もあります。一般的なアプローチには、次のようなものがあります。

§実際のWebサービスに対してテストする

これはもちろん、クライアントコードに対する最も高いレベルの信頼性を得られますが、通常は現実的ではありません。サードパーティのWebサービスの場合、テストの実行を妨げるレート制限がある可能性があり(サードパーティのサービスに対して自動テストを実行することは、良いネット市民とはみなされません)、テストに必要なデータを設定したり、そのサービス上に必要なデータが存在することを保証したりすることができない場合があり、テストがサービスに望ましくない副作用をもたらす可能性があります。

§Webサービスのテストインスタンスに対してテストする

これは前のものよりは少し良いですが、それでもいくつかの問題があります。多くのサードパーティのWebサービスはテストインスタンスを提供していません。また、テストがテストインスタンスの実行に依存することを意味し、テストサービスがビルドを失敗させる可能性があります。テストインスタンスがファイアウォールの内側にある場合、テストを実行できる場所も制限されます。

§HTTPクライアントをモックする

このアプローチは、テストコードに対する信頼性が最も低くなります。多くの場合、この種のテストは、コードがその実行内容以上のことをテストするのに過ぎず、価値がありません。モックのWebサービスクライアントに対するテストは、コードが実行されて特定のことを行うことを示しますが、コードが実行する内容が実際に有効なHTTPリクエストの作成と相関するかどうかについては、まったく信頼性を提供しません。

§Webサービスをモックする

このアプローチは、実際のWebサービスに対するテストとHTTPクライアントのモックの間の良い妥協点です。テストでは、作成するすべてのリクエストが有効なHTTPリクエストであること、ボディのシリアライズ/デシリアライズが機能することなどが示されますが、それらは完全に自己完結型であり、サードパーティのサービスには依存しません。

Playは、テストでWebサービスをモックするためのいくつかのヘルパーユーティリティを提供しており、このテストアプローチを非常に実行可能で魅力的なオプションにしています。

§GitHubクライアントのテスト

例として、GitHubクライアントを作成し、それをテストしたいとしましょう。クライアントは非常にシンプルで、パブリックリポジトリの名前を検索するだけです。

import com.fasterxml.jackson.databind.JsonNode;
import java.util.*;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;
import javax.inject.Inject;
import play.libs.ws.WSClient;

class GitHubClient {
  private WSClient ws;

  @Inject
  public GitHubClient(WSClient ws) {
    this.ws = ws;
  }

  String baseUrl = "https://api.github.com";

  public CompletionStage<List<String>> getRepositories() {
    return ws.url(baseUrl + "/repositories")
        .get()
        .thenApply(
            response ->
                response.asJson().findValues("full_name").stream()
                    .map(JsonNode::asText)
                    .collect(Collectors.toList()));
  }
}

GitHub APIのベースURLをパラメータとして受け取ることに注意してください。テストではこれをオーバーライドして、モックサーバーを指すようにします。

これをテストするには、このエンドポイントを実装する埋め込みPlayサーバーが必要です。埋め込みサーバーを作成するには、ルーティングDSLを使用します。

Server server =
    Server.forRouter(
        (components) ->
            RoutingDsl.fromComponents(components)
                .GET("/repositories")
                .routingTo(
                    request -> {
                      ArrayNode repos = Json.newArray();
                      ObjectNode repo = Json.newObject();
                      repo.put("full_name", "octocat/Hello-World");
                      repos.add(repo);
                      return ok(repos);
                    })
                .build());

サーバーは現在、httpPortメソッドでアクセスできるランダムなポートで実行されています。これを使用してGitHubClientに渡すベースURLを作成できますが、Playにはさらにシンプルなメカニズムがあります。WSTestClientクラスは、ポート番号を受け取るnewClientメソッドを提供します。クライアントを使用して相対URL(例:/repositories)へのリクエストが作成されると、このクライアントは、渡されたポートでlocalhostにリクエストを送信します。これは、GitHubClientのベースURLを""に設定できることを意味します。また、クライアントが、クライアントがさらにリクエストを行うために使用する他のリソースへのURLリンクを含むリソースを返す場合は、それらが相対URLであることを確認し、そのまま使用できます。

これで、@Beforeアノテーション付きメソッドでサーバー、WSクライアント、GitHubClientを作成し、@Afterアノテーション付きメソッドでシャットダウンし、テストでクライアントをテストできます。

import static org.hamcrest.core.IsCollectionContaining.*;
import static org.junit.Assert.*;
import static play.mvc.Results.*;

import com.fasterxml.jackson.databind.node.*;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import org.junit.*;
import play.libs.Json;
import play.libs.ws.*;
import play.routing.RoutingDsl;
import play.server.Server;

public class GitHubClientTest {
  private GitHubClient client;
  private WSClient ws;
  private Server server;

  @Before
  public void setup() {
    server =
        Server.forRouter(
            (components) ->
                RoutingDsl.fromComponents(components)
                    .GET("/repositories")
                    .routingTo(
                        request -> {
                          ArrayNode repos = Json.newArray();
                          ObjectNode repo = Json.newObject();
                          repo.put("full_name", "octocat/Hello-World");
                          repos.add(repo);
                          return ok(repos);
                        })
                    .build());
    ws = play.test.WSTestClient.newClient(server.httpPort());
    client = new GitHubClient(ws);
    client.baseUrl = "";
  }

  @After
  public void tearDown() throws IOException {
    try {
      ws.close();
    } finally {
      server.stop();
    }
  }

  @Test
  public void repositories() throws Exception {
    List<String> repos = client.getRepositories().toCompletableFuture().get(10, TimeUnit.SECONDS);
    assertThat(repos, hasItem("octocat/Hello-World"));
  }
}

§ファイルの返却

前の例では、モックサービスのJSONを手動で構築しました。テストするサービスからの実際のレスポンスをキャプチャして、それを返す方が良いことがよくあります。これを支援するために、Playは、クラスパス上のファイルから簡単に結果を作成できるsendResourceメソッドを提供します。

したがって、実際のGitHub APIに対してリクエストを行った後、テストリソースディレクトリに保存するファイルを作成します。テストリソースディレクトリは、Playディレクトリレイアウトを使用している場合はtest/resources、標準のsbtディレクトリレイアウトを使用している場合はsrc/test/resourcesです。この場合、github/repositories.jsonと呼び、次のものが含まれます。

[
  {
    "id": 1296269,
    "owner": {
      "login": "octocat",
      "id": 1,
      "avatar_url": "https://github.com/images/error/octocat_happy.gif",
      "gravatar_id": "",
      "url": "https://api.github.com/users/octocat",
      "html_url": "https://github.com/octocat",
      "followers_url": "https://api.github.com/users/octocat/followers",
      "following_url": "https://api.github.com/users/octocat/following{/other_user}",
      "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
      "organizations_url": "https://api.github.com/users/octocat/orgs",
      "repos_url": "https://api.github.com/users/octocat/repos",
      "events_url": "https://api.github.com/users/octocat/events{/privacy}",
      "received_events_url": "https://api.github.com/users/octocat/received_events",
      "type": "User",
      "site_admin": false
    },
    "name": "Hello-World",
    "full_name": "octocat/Hello-World",
    "description": "This your first repo!",
    "private": false,
    "fork": false,
    "url": "https://api.github.com/repos/octocat/Hello-World",
    "html_url": "https://github.com/octocat/Hello-World"
  }
]

テストのニーズに合わせて変更できます。たとえば、GitHubクライアントが上記のレスポンスのURLを使用して他のエンドポイントにリクエストを行う場合、それらのURLからhttps://api.github.comプレフィックスを削除して、それらも相対的になるようにし、テストクライアントによって正しいポートのlocalhostに自動的にルーティングされるようにすることができます。

次に、このリソースを提供するようにルーターを変更します。

Server server =
    Server.forRouter(
        (components) ->
            RoutingDsl.fromComponents(components)
                .GET("/repositories")
                .routingTo(request -> ok().sendResource("github/repositories.json"))
                .build());

Playは、ファイル名の拡張子が.jsonであるため、コンテンツタイプをapplication/jsonに自動的に設定することに注意してください。

次: ロギング


このドキュメントにエラーが見つかりましたか?このページのソースコードはこちらにあります。ドキュメントのガイドラインを読んだ後、プルリクエストを自由に投稿してください。質問や共有するアドバイスがありますか?コミュニティフォーラムにアクセスして、コミュニティと会話を始めてください。