モック API
はじめに
Web APIは通常、HTTPエンドポイントとして実装されます。Playwrightは、HTTPとHTTPSの両方のネットワークトラフィックを**モック**および**変更**するためのAPIを提供します。XHRやfetchリクエストを含む、ページが行うすべてのリクエストを追跡、変更、モックできます。Playwrightを使用すると、ページによって行われた複数のネットワークリクエストを含むHARファイルを使用してモックすることもできます。
APIリクエストをモックする
次のコードは、*/**/api/v1/fruitsへのすべての呼び出しをインターセプトし、代わりにカスタム応答を返します。APIへのリクエストは行われません。テストはモックされたルートを使用するURLに移動し、モックデータがページに存在することを確認します。
// Intercept the route to the fruit API
page.route("https://fruit.ceo/api/breeds/image/random", route -> {
    List<Dictionary<String, Object>> data = new ArrayList<Dictionary<String, Object>>();
    Hashtable<String, Object> dict = new Hashtable<String, Object>();
    dict.put("name", "Strawberry");
    dict.put("id", 21);
    data.add(dict);
  // fulfill the route with the mock data
  route.fulfill(RequestOptions.create().setData(data));
});
// Go to the page
page.navigate("https://demo.playwright.dev/api-mocking");
// Assert that the Strawberry fruit is visible
assertThat(page.getByText("Strawberry")).isVisible();
APIは呼び出されず、モックデータで満たされたことが、例のテストのトレースからわかります。
高度なネットワークについて詳しくはこちらをご覧ください。
API応答を変更する
場合によっては、APIリクエストを行う必要がありますが、再現可能なテストを可能にするために応答をパッチする必要がある場合があります。この場合、リクエストをモックする代わりに、リクエストを実行し、変更された応答でそれを満たすことができます。
以下の例では、果物APIへの呼び出しをインターセプトし、「Loquat」という新しい果物をデータに追加します。次に、URLに移動し、このデータが存在することを確認します。
page.route("*/**/api/v1/fruits", route -> {
  Response response = route.fetch();
  byte[] json = response.body();
  JsonObject parsed = new Gson().fromJson(new String(json), JsonObject.class);
  parsed.add(new JsonObject().add("name", "Loquat").add("id", 100));
  // Fulfill using the original response, while patching the response body
  // with the given JSON object.
  route.fulfill(new Route.FulfillOptions().setResponse(response).setBody(parsed.toString()));
});
// Go to the page
page.navigate("https://demo.playwright.dev/api-mocking");
// Assert that the Loquat fruit is visible
assertThat(page.getByText("Loquat", new Page.GetByTextOptions().setExact(true))).isVisible();
テストのトレースでは、APIが呼び出され、応答が変更されたことがわかります。
応答を検査すると、新しい果物がリストに追加されたことがわかります。
高度なネットワークについて詳しくはこちらをご覧ください。
HARファイルによるモック
HARファイルは、ページが読み込まれたときに作成されたすべてのネットワークリクエストの記録を含むHTTPアーカイブファイルです。リクエストと応答のヘッダー、Cookie、コンテンツ、タイミングなどに関する情報が含まれています。HARファイルを使用して、テストでネットワークリクエストをモックできます。次の手順を実行する必要があります。
- HARファイルを記録します。
- HARファイルをテストと一緒にコミットします。
- テストで保存されたHARファイルを使用してリクエストをルーティングします。
HARファイルの記録
HARファイルを記録するには、Page.routeFromHAR() または BrowserContext.routeFromHAR() メソッドを使用します。このメソッドは、HARファイルへのパスとオプションのオブジェクトを受け取ります。オプションオブジェクトにはURLを含めることができ、指定されたグロブパターンに一致するURLを持つリクエストのみがHARファイルから提供されます。指定しない場合、すべてのリクエストがHARファイルから提供されます。
updateオプションをtrueに設定すると、HARファイルからリクエストを提供する代わりに、実際のネットワーク情報でHARファイルが作成または更新されます。テストを作成してHARに実際のデータを入力するときに使用します。
または、ブラウザコンテキストを作成する際にBrowser.newContext()のsetRecordHarPathオプションを使用してHARファイルを記録することもできます。これにより、コンテキストが閉じられるまで、コンテキスト全体のすべてのネットワークトラフィックをキャプチャできます。
// Get the response from the HAR file
page.routeFromHAR(Path.of("./hars/fruit.har"), new RouteFromHAROptions()
  .setUrl("*/**/api/v1/fruits")
  .setUpdate(true)
);
// Go to the page
page.navigate("https://demo.playwright.dev/api-mocking");
// Assert that the fruit is visible
assertThat(page.getByText("Strawberry")).isVisible();
HARファイルの変更
HARファイルを記録したら、「hars」フォルダ内のハッシュされた.txtファイルを開き、JSONを編集して変更できます。このファイルはソース管理にコミットする必要があります。update: trueでこのテストを実行するたびに、APIからのリクエストでHARファイルが更新されます。
[
  {
    "name": "Playwright",
    "id": 100
  },
  // ... other fruits
]
HARからのリプレイ
HARファイルを記録し、モックデータを変更したので、テストで一致する応答を提供するために使用できます。このためには、updateオプションをオフにするか、単に削除するだけです。これにより、APIを呼び出す代わりに、HARファイルに対してテストが実行されます。
// Replay API requests from HAR.
// Either use a matching response from the HAR,
// or abort the request if nothing matches.
page.routeFromHAR(Path.of("./hars/fruit.har"), new RouteFromHAROptions()
  .setUrl("*/**/api/v1/fruits")
  .setUpdate(false)
);
// Go to the page
page.navigate("https://demo.playwright.dev/api-mocking");
// Assert that the Playwright fruit is visible
assertThat(page.getByText("Playwright", new Page.GetByTextOptions()
  .setExact(true))).isVisible();
テストのトレースでは、ルートがHARファイルから満たされ、APIが呼び出されなかったことがわかります。
応答を検査すると、新しい果物がJSONに追加されたことがわかります。これは、「hars」フォルダ内のハッシュされた.txtファイルを手動で更新することによって行われました。
HARリプレイはURLとHTTPメソッドに厳密に一致します。POSTリクエストの場合、POSTペイロードにも厳密に一致します。複数の記録がリクエストに一致する場合、最も多くのヘッダーが一致するものが選択されます。リダイレクトを伴うエントリは自動的に追跡されます。
記録時と同様に、与えられたHARファイル名が.zipで終わる場合、それはHARファイルと別々のエントリとして保存されたネットワークペイロードを含むアーカイブと見なされます。このアーカイブを解凍し、ペイロードまたはHARログを手動で編集し、解凍されたHARファイルを指すこともできます。すべてのペイロードは、ファイルシステム上の解凍されたHARファイルに対する相対パスで解決されます。
CLIによるHARの記録
テストのHARファイルを記録するには、updateオプションを使用することをお勧めします。ただし、Playwright CLIでHARを記録することもできます。
Playwright CLIでブラウザを開き、--save-harオプションを渡してHARファイルを生成します。オプションで、--save-har-globを使用して、APIエンドポイントなど、関心のあるリクエストのみを保存します。HARファイル名が.zipで終わる場合、アーティファクトは別々のファイルとして書き込まれ、すべて単一のzipに圧縮されます。
# Save API requests from example.com as "example.har" archive.
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="open --save-har=example.har --save-har-glob='**/api/**' https://example.com"
高度なネットワークについて詳しくはこちらをご覧ください。
WebSocketをモックする
次のコードは、WebSocket接続をインターセプトし、サーバーに接続する代わりにWebSocket上の通信全体をモックします。この例では、"request"に対して"response"で応答します。
page.routeWebSocket("wss://example.com/ws", ws -> {
  ws.onMessage(frame -> {
    if ("request".equals(frame.text()))
      ws.send("response");
  });
});
あるいは、実際のサーバーに接続したいが、途中のメッセージをインターセプトして変更したりブロックしたりしたい場合もあります。これは、ページからサーバーに送信されたメッセージの一部を変更し、残りのメッセージは変更しない例です。
page.routeWebSocket("wss://example.com/ws", ws -> {
  WebSocketRoute server = ws.connectToServer();
  ws.onMessage(frame -> {
    if ("request".equals(frame.text()))
      server.send("request2");
    else
      server.send(frame.text());
  });
});
詳細については、WebSocketRouteをご覧ください。