§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
に自動的に設定することに注意してください。
次: ロギング
このドキュメントにエラーが見つかりましたか?このページのソースコードはこちらにあります。ドキュメントのガイドラインを読んだ後、プルリクエストを自由に投稿してください。質問や共有するアドバイスがありますか?コミュニティフォーラムにアクセスして、コミュニティと会話を始めてください。