モック API
はじめに
Web API は通常、HTTP エンドポイントとして実装されます。Playwright は、HTTP および HTTPS の両方のネットワークトラフィックをモックおよび変更するための API を提供します。ページが行うリクエスト(XHR や fetch リクエストなど)は、追跡、変更、およびモックが可能です。Playwright では、ページによって行われた複数のネットワークリクエストを含む HAR ファイルを使用してモックすることもできます。
API リクエストのモック
次のコードは、*/**/api/v1/fruits
へのすべての呼び出しをインターセプトし、代わりにカスタムレスポンスを返します。API へのリクエストは行われません。テストは、モックされたルートを使用する URL にアクセスし、モックデータがページに存在することを確認します。
- 同期
- 非同期
def test_mock_the_fruit_api(page: Page):
def handle(route: Route):
json = [{"name": "Strawberry", "id": 21}]
# fulfill the route with the mock data
route.fulfill(json=json)
# Intercept the route to the fruit API
page.route("*/**/api/v1/fruits", handle)
# Go to the page
page.goto("https://demo.playwright.dev/api-mocking")
# Assert that the Strawberry fruit is visible
expect(page.get_by_text("Strawberry")).to_be_visible()
async def test_mock_the_fruit_api(page: Page):
async def handle(route: Route):
json = [{"name": "Strawberry", "id": 21}]
# fulfill the route with the mock data
await route.fulfill(json=json)
# Intercept the route to the fruit API
await page.route("*/**/api/v1/fruits", handle)
# Go to the page
await page.goto("https://demo.playwright.dev/api-mocking")
# Assert that the Strawberry fruit is visible
await expect(page.get_by_text("Strawberry")).to_be_visible()
サンプルテストのトレースから、API が一度も呼び出されず、モックデータで fulfilled されたことがわかります。
高度なネットワークの詳細はこちらをご覧ください。
API レスポンスの変更
再現性のあるテストを可能にするために、API リクエストを行う必要があるものの、レスポンスを修正する必要がある場合があります。その場合、リクエストをモックする代わりに、リクエストを実行し、変更されたレスポンスで fulfill することができます。
以下の例では、fruit API への呼び出しをインターセプトし、'Loquat' という新しいフルーツをデータに追加します。その後、URL にアクセスし、このデータがあることを確認します
- 同期
- 非同期
def test_gets_the_json_from_api_and_adds_a_new_fruit(page: Page):
def handle(route: Route):
response = route.fetch()
json = response.json()
json.append({ "name": "Loquat", "id": 100})
# Fulfill using the original response, while patching the response body
# with the given JSON object.
route.fulfill(response=response, json=json)
page.route("https://demo.playwright.dev/api-mocking/api/v1/fruits", handle)
# Go to the page
page.goto("https://demo.playwright.dev/api-mocking")
# Assert that the new fruit is visible
expect(page.get_by_text("Loquat", exact=True)).to_be_visible()
async def test_gets_the_json_from_api_and_adds_a_new_fruit(page: Page):
async def handle(route: Route):
response = await route.fetch()
json = await response.json()
json.append({ "name": "Loquat", "id": 100})
# Fulfill using the original response, while patching the response body
# with the given JSON object.
await route.fulfill(response=response, json=json)
await page.route("https://demo.playwright.dev/api-mocking/api/v1/fruits", handle)
# Go to the page
await page.goto("https://demo.playwright.dev/api-mocking")
# Assert that the new fruit is visible
await expect(page.get_by_text("Loquat", exact=True)).to_be_visible()
テストのトレースでは、API が呼び出され、レスポンスが変更されたことがわかります。
レスポンスを検査すると、新しいフルーツがリストに追加されたことがわかります。
高度なネットワークの詳細はこちらをご覧ください。
HAR ファイルを使用したモック
HAR ファイルは、ページがロードされたときに行われたすべてのネットワークリクエストの記録を含む HTTP Archive ファイルです。リクエストとレスポンスのヘッダー、クッキー、コンテンツ、タイミングなどの情報が含まれています。テストで HAR ファイルを使用してネットワークリクエストをモックできます。以下の手順が必要です。
- HAR ファイルを記録する。
- HAR ファイルをテストとともにコミットする。
- テストで保存された HAR ファイルを使用してリクエストをルーティングする。
HAR ファイルの記録
HAR ファイルを記録するには、page.route_from_har() または browser_context.route_from_har() メソッドを使用します。このメソッドは、HAR ファイルへのパスとオプションのオブジェクトを受け取ります。オプションオブジェクトには、URL を含めることができ、指定された glob パターンに一致する URL を持つリクエストのみが HAR ファイルから提供されます。指定しない場合、すべてのリクエストは HAR ファイルから提供されます。
update
オプションを true に設定すると、リクエストを HAR ファイルから提供する代わりに、実際のネットワーク情報で HAR ファイルを作成または更新します。実際のデータで HAR を作成するテストを作成するときに使用します。
- 同期
- 非同期
def test_records_or_updates_the_har_file(page: Page):
# Get the response from the HAR file
page.route_from_har("./hars/fruit.har", url="*/**/api/v1/fruits", update=True)
# Go to the page
page.goto("https://demo.playwright.dev/api-mocking")
# Assert that the fruit is visible
expect(page.get_by_text("Strawberry")).to_be_visible()
async def test_records_or_updates_the_har_file(page: Page):
# Get the response from the HAR file
await page.route_from_har("./hars/fruit.har", url="*/**/api/v1/fruits", update=True)
# Go to the page
await page.goto("https://demo.playwright.dev/api-mocking")
# Assert that the fruit is visible
await expect(page.get_by_text("Strawberry")).to_be_visible()
HAR ファイルの変更
HAR ファイルを記録したら、'hars' フォルダ内のハッシュ化された .txt ファイルを開き、JSON を編集することで変更できます。このファイルはソース管理にコミットする必要があります。update: true
でこのテストを実行するたびに、API からのリクエストで HAR ファイルが更新されます。
[
{
"name": "Playwright",
"id": 100
},
// ... other fruits
]
HAR からのリプレイ
HAR ファイルを記録し、モックデータを変更したので、テストで一致するレスポンスを提供するために使用できます。これを行うには、update
オプションをオフにするか、単に削除してください。これにより、API にアクセスする代わりに、HAR ファイルに対してテストが実行されます。
- 同期
- 非同期
def test_gets_the_json_from_har_and_checks_the_new_fruit_has_been_added(page: Page):
# Replay API requests from HAR.
# Either use a matching response from the HAR,
# or abort the request if nothing matches.
page.route_from_har("./hars/fruit.har", url="*/**/api/v1/fruits", update=False)
# Go to the page
page.goto("https://demo.playwright.dev/api-mocking")
# Assert that the Playwright fruit is visible
expect(page.get_by_text("Playwright", exact=True)).to_be_visible()
async def test_gets_the_json_from_har_and_checks_the_new_fruit_has_been_added(page: Page):
# Replay API requests from HAR.
# Either use a matching response from the HAR,
# or abort the request if nothing matches.
await page.route_from_har("./hars/fruit.har", url="*/**/api/v1/fruits", update=False)
# Go to the page
await page.goto("https://demo.playwright.dev/api-mocking")
# Assert that the Playwright fruit is visible
await expect(page.get_by_text("Playwright", exact=True)).to_be_visible()
テストのトレースでは、ルートが HAR ファイルから fulfilled され、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
で終わる場合、アーティファクトは個別のファイルとして書き込まれ、すべて 1 つの zip
に圧縮されます。
# Save API requests from example.com as "example.har" archive.
playwright open --save-har=example.har --save-har-glob="**/api/**" https://example.com
高度なネットワークの詳細はこちらをご覧ください。
WebSocket のモック
次のコードは、WebSocket 接続をインターセプトし、サーバーに接続する代わりに、WebSocket を介した通信全体をモックします。この例では、"request"
に "response"
で応答します。
- 同期
- 非同期
def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):
if message == "request":
ws.send("response")
page.route_web_socket("wss://example.com/ws", lambda ws: ws.on_message(
lambda message: message_handler(ws, message)
))
def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):
if message == "request":
ws.send("response")
await page.route_web_socket("wss://example.com/ws", lambda ws: ws.on_message(
lambda message: message_handler(ws, message)
))
または、実際のサーバーに接続したいが、途中のメッセージをインターセプトして変更またはブロックしたい場合があります。これは、ページからサーバーに送信されたメッセージの一部を変更し、残りを変更しないままにする例です。
- 同期
- 非同期
def message_handler(server: WebSocketRoute, message: Union[str, bytes]):
if message == "request":
server.send("request2")
else:
server.send(message)
def handler(ws: WebSocketRoute):
server = ws.connect_to_server()
ws.on_message(lambda message: message_handler(server, message))
page.route_web_socket("wss://example.com/ws", handler)
def message_handler(server: WebSocketRoute, message: Union[str, bytes]):
if message == "request":
server.send("request2")
else:
server.send(message)
def handler(ws: WebSocketRoute):
server = ws.connect_to_server()
ws.on_message(lambda message: message_handler(server, message))
await page.route_web_socket("wss://example.com/ws", handler)
詳細については、WebSocketRoute を参照してください。