PlaywrightのE2Eテストは「ログインボタンをクリックできる」「ダッシュボードが表示される」「チャートが描画される」ことを確認できます。しかし、APIが200 OKを返していても、レスポンスの数値やスキーマが壊れていれば、UIテストだけでは見逃すことがあります。たとえばチャートが表示されても、集計値が間違っていればプロダクト上はバグです。このギャップを埋めるには、Playwrightのブラウザテストに加えて、APIコントラクト、スキーマ、レスポンスの意味まで検証する必要があります。Apidogのようなツールを併用すると、UIフローとAPIシナリオを同じCIで実行できます。
要約
Playwrightのrequestフィクスチャとpage.routeを使うと、E2Eテスト内でAPIレスポンスを検証できます。
ただし、Playwrightだけで大規模なAPI検証を管理するのは難しくなります。実装では次の構成が扱いやすいです。
-
openapi.yamlをAPIコントラクトの唯一の情報源にする - PlaywrightではUIフローと薄いAPIスモークチェックを実行する
- Apidogではスキーマ検証、連鎖APIシナリオ、エラーパスを検証する
- PlaywrightとApidogで同じフィクスチャとサンプルペイロードを共有する
- CIで両方のスイートを並列実行する
はじめに
Playwrightはブラウザ自動化に強く、PlaywrightのAPIテストドキュメントにもあるように、request.get()やexpect(response.status()).toBe(200)でAPIを直接叩けます。
ただし、実運用では次の問題が起きます。
- ステータスコードだけを見て、レスポンススキーマを検証していない
- UIテストとAPIテストで別々のフィクスチャを持っている
- API仕様の変更にテストが追従できない
- バックエンドが壊れているとフロントエンド開発が止まる
- エラーパスや連鎖リクエストを十分に検証できない
解決策は、OpenAPI仕様を契約として扱うことです。
openapi.yamlを中心にして、Playwrightのrequest呼び出し、page.routeによるモック、Apidogシナリオを同じ仕様から駆動します。Apidogをローカルで使う場合は、先にApidogをダウンロードしておくと進めやすくなります。
テストツール選定の背景を知りたい場合は、QAエンジニア向けのAPIテストツールも参考になります。
PlaywrightテストとAPIアサーションのギャップ
典型的なPlaywrightテストは、次のような流れになります。
- ログインする
- ページへ移動する
- UI要素が表示されることを確認する
- ボタンやフォーム操作が成功することを確認する
これはユーザー視点の検証として有効です。しかし、APIの中身までは十分に保証できません。
見逃しやすい障害は主に3つあります。
1. ペイロード形状の退行
APIは200を返しているが、レスポンスのフィールド名が変わっているケースです。
例:
{
"total_count": 120
}
が次のように変わる。
{
"totalCount": 120
}
UI側がフォールバックしてゼロを表示したり、型変換で表面上動いてしまったりすると、Playwrightの画面確認だけでは検出しにくくなります。
2. ビジネスロジックのずれ
たとえば、割引APIが契約上は15%割引を返すべきなのに、実際には10%を返しているケースです。
UIはAPIの値をそのまま表示するため、「画面に割引額が表示されている」ことだけを検証していると、誤った金額でもテストが通ります。
3. エラーパスの不足
PlaywrightのE2Eテストはハッピーパスに偏りがちです。
一方、APIには次のようなエラーパスがあります。
- レート制限
- 期限切れトークン
- 権限不足
- 部分的失敗
- 冪等性キーの衝突
- Webhook署名エラー
- 外部サービスのタイムアウト
これらは専用のAPIテストシナリオで検証する方が管理しやすくなります。
役割分担は次のようにします。
- Playwright: UIフロー、ブラウザ操作、ネットワークインターセプション、主要APIの薄いスモークチェック
- Apidog: スキーマ準拠、連鎖APIワークフロー、コントラクト遵守、エラーパス検証
両方のスイートが同じOpenAPI仕様を参照すれば、API契約の情報源を1つにできます。設計優先の進め方については、設計優先APIワークフローも参考になります。
PlaywrightとApidogでフィクスチャを共有する
まず、リポジトリのルートにOpenAPI仕様を置きます。
.
├── openapi.yaml
├── fixtures/
│ └── order.json
├── tests/
│ ├── fixtures/
│ │ └── api.ts
│ └── orders.spec.ts
└── playwright.config.ts
openapi.yamlを唯一の契約として扱い、PlaywrightとApidogの両方から参照します。
Playwright用のAPIフィクスチャを作る
// tests/fixtures/api.ts
import { test as base, APIRequestContext, expect } from '@playwright/test';
import { readFileSync } from 'fs';
import path from 'path';
type ApiFixtures = {
apiRequest: APIRequestContext;
authToken: string;
sampleOrder: Record<string, unknown>;
};
export const test = base.extend<ApiFixtures>({
apiRequest: async ({ playwright }, use) => {
const ctx = await playwright.request.newContext({
baseURL: process.env.API_BASE_URL ?? 'https://api.staging.example.com',
extraHTTPHeaders: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
await use(ctx);
await ctx.dispose();
},
authToken: async ({ apiRequest }, use) => {
const res = await apiRequest.post('/auth/token', {
data: {
email: 'qa@example.com',
password: process.env.QA_PASSWORD,
},
});
expect(res.status()).toBe(200);
const body = await res.json();
await use(body.access_token);
},
sampleOrder: async ({}, use) => {
const raw = readFileSync(
path.join(__dirname, '..', '..', 'fixtures', 'order.json'),
'utf8',
);
await use(JSON.parse(raw));
},
});
export { expect };
これで各テストでは、@playwright/testではなく、このフィクスチャファイルからtestとexpectをインポートします。
// tests/orders.spec.ts
import { test, expect } from './fixtures/api';
test('POST /orders returns a valid order with 15 percent discount', async ({
apiRequest,
authToken,
sampleOrder,
}) => {
const res = await apiRequest.post('/orders', {
headers: {
Authorization: `Bearer ${authToken}`,
},
data: {
...sampleOrder,
coupon: 'SAVE15',
},
});
expect(res.status()).toBe(201);
const body = await res.json();
expect(body).toMatchObject({
id: expect.any(String),
status: 'pending',
discount_pct: 15,
total_cents: expect.any(Number),
});
expect(body.total_cents).toBeLessThan(sampleOrder.subtotal_cents);
});
このテストでは、単に201を確認するだけではなく、ビジネス上重要なdiscount_pct: 15も検証しています。
Apidog側で同じ仕様とフィクスチャを使う
Apidogでは次の流れで設定します。
- Apidogプロジェクトを開く
-
openapi.yamlをインポートする - エンドポイント、リクエスト例、パラメータースキーマを生成する
-
fixtures/order.jsonと同じ内容を環境変数またはデータセットとして登録する -
POST /ordersのシナリオを作成する - レスポンスを
Orderスキーマに対して検証する
Playwrightでは重要なセマンティックアサーションを行い、ApidogではOpenAPIスキーマに基づく深い検証を実行します。
Postmanからの移行を検討している場合は、セルフホスト型Postman代替も参考になります。
Apidog + Playwrightワークフローのセットアップ
ここでは、実装手順をCIまで含めて整理します。
ステップ1:OpenAPI仕様をリポジトリに置く
openapi.yamlをリポジトリルートに配置します。
openapi.yaml
運用ルールは次のようにします。
- PRレビュー必須にする
- 破壊的変更は明示する
- API実装より先に仕様を更新する
- PlaywrightとApidogの両方が同じ仕様を使う
既存APIから始める場合は、FastAPIやNestJSなどのOpenAPI出力を使ってドラフトを生成し、手動で整理します。ApidogではHARファイルから仕様を作成する運用も可能です。
ステップ2:Playwrightを構成する
Playwrightを追加します。
npm init playwright@latest
package.jsonにスクリプトを追加します。
{
"scripts": {
"test:e2e": "playwright test"
}
}
playwright.config.tsではステージング環境を向けます。
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
baseURL: process.env.WEB_BASE_URL ?? 'https://staging.example.com',
trace: 'on-first-retry',
},
retries: 2,
});
ステップ3:Apidogでシナリオを作る
Apidogでは、重要なユーザージャーニーごとにシナリオを作ります。
例:
- サインアップ
- ログイン
- チェックアウト
- 注文作成
- 返金
- Webhook配信
- トークン期限切れ
- レート制限
各シナリオでは、API呼び出しを連鎖させ、レスポンスの値を次のステップに渡します。
Apidog CLIで実行できるように、シナリオをJSONとして管理します。
apidog-cli run ./apidog/scenarios/checkout.json
ステップ4:PlaywrightでAPIをスタブする
バックエンドに依存せずUIを確認したい場合は、page.routeを使います。
test('dashboard renders cached order list when offline', async ({
page,
sampleOrder,
}) => {
await page.route('**/api/orders', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
orders: [sampleOrder],
}),
});
});
await page.goto('/dashboard');
await expect(page.getByTestId('order-row')).toHaveCount(1);
});
ポイントは、スタブ用のレスポンスもfixtures/order.jsonから作ることです。
これにより、PlaywrightのUIテストとApidogのAPIシナリオで同じデータを使えます。
ステップ5:CIで両方のスイートを実行する
GitHub Actionsでは、PlaywrightとApidogを別ジョブで並列実行します。
name: tests
on: [push, pull_request]
jobs:
playwright:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
apidog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm i -g apidog-cli
- run: apidog-cli run ./apidog/scenarios/checkout.json --reporters cli,junit
どちらかが失敗したらマージを止めます。
--reporters junitを使うと、CI上で失敗したアサーションを確認しやすくなります。GitHub Actionsのドキュメントには、マトリックスビルドやキャッシュの設定も載っています。
専任QAがいないチームでは、QAエンジニア向けのAPIテストツールのように、責務をUIとAPIで分けると運用しやすくなります。
ステップ6:仕様ドリフトを検出する
API仕様が実装とずれると、テストが通っていても本番で壊れる可能性があります。
次のようなチェックを追加します。
- 最新の
openapi.yamlをCIで検証する - 前回リリースとの差分を確認する
- フィールド削除や型変更を破壊的変更として扱う
- 破壊的変更があればビルドを失敗させる
これにより、「200 OKだがペイロードが間違っている」タイプのバグを早期に捕捉できます。
高度なテクニックと実装上のヒント
Playwright Trace Viewerを有効にする
playwright.config.tsで次を設定します。
export default defineConfig({
use: {
trace: 'on-first-retry',
},
});
不安定なテストが失敗したときに、次を確認できます。
- ネットワーク呼び出し
- DOMスナップショット
- コンソールログ
- ユーザー操作のタイムライン
API側では、Apidog CLIのHTMLレポートと組み合わせると、UIが壊れたのかAPIが壊れたのかを切り分けやすくなります。
Apidogモックサーバーを使う
バックエンドが未完成、デプロイ中、またはステージングDBが不安定な場合は、Apidogのモックサーバーを使います。
実装例:
- OpenAPI仕様をApidogにインポートする
- モックサーバーを起動する
- Playwrightの
API_BASE_URLをモックサーバーに向ける - UIテストはモックに対して実行する
- Apidogシナリオは実バックエンドに対して実行する
モックを使ったテスト生成については、AI支援APIテスト生成も参考になります。
リトライを増やしすぎない
Playwrightでは次の程度に抑えます。
export default defineConfig({
retries: 2,
});
3回以上リトライしないと通らないテストは、テストまたは環境に問題があります。
Apidogシナリオでも、リクエスト単位のリトライは最小限にします。
スキーマ不一致は失敗として扱う
スキーマの不一致を警告で済ませると、API契約の価値が下がります。
どうしても一時的に許容する場合は、環境変数で明示します。
ALLOW_SCHEMA_DRIFT=true
ただし、その理由をPR上で説明する運用にします。
テストを優先度で分ける
全テストを毎回実行するとCIが遅くなります。
おすすめは次の分け方です。
-
@smoke: すべてのpushで実行 -
@regression: mainブランチへのPRで実行 -
@nightly: 夜間に全件実行
Playwrightではタグやgrepを使えます。
npx playwright test --grep @smoke
状態を共有するフローは、必要に応じて直列実行にします。
test.describe.configure({ mode: 'serial' });
避けるべきミス
-
status === 200だけを検証する - ベアラートークンをフィクスチャにハードコードする
- PlaywrightとApidogで別々のサンプルJSONを持つ
- Apidog CLIをCIから外す
-
page.routeのスタブを本物のAPIテストの代替にする - OpenAPI仕様を更新せずに実装だけ変える
AI関連APIを検証する場合は、非決定的なレスポンスも考慮する必要があります。AIエージェントAPIのテスト方法が参考になります。
代替ツールと比較
ブラウザテストとAPIテストを組み合わせる方法はいくつかあります。
| スタック | 強み | 弱み | 最適な用途 |
|---|---|---|---|
Playwright単体(requestフィクスチャ) |
単一ツール、高速、Playwrightに統合しやすい | スキーマ検証が浅い、連鎖シナリオが弱い、エラーパスを網羅しにくい | 小規模チーム、シンプルなAPI |
| Playwright + Postman | Postmanエコシステム、Newman CLI | OpenAPIとコレクションが乖離しやすい、情報源が分かれやすい | すでにPostmanを深く使っているチーム |
| Playwright + Apidog | OpenAPIを単一ソースにできる、スキーマ検証、モック、CI用CLI、設計優先ワークフロー | PlaywrightとApidogの両方を覚える必要がある | 仕様駆動でAPIとUIの両方を検証したいチーム |
| Cypress + cy-apiプラグイン | Cypressユーザーには導入しやすい | APIテストの表現力が限定される場合がある | 既存Cypressコードベース |
| Pact | サービス間契約に強い | 学習コストが高い、ブローカーが必要、UIテストではない | 多数の内部APIコンシューマーを持つマイクロサービス組織 |
SOAPや古いAPIテスト基盤から移行する場合は、SoapUI Groovyスクリプトの代替とReadyAPIの代替も参考になります。
ローカル中心の開発フローでは、RESTクライアントVSCode拡張機能も選択肢になります。
実世界のユースケース
Eコマースのチェックアウト
小売チームでは、Playwrightでカートから注文完了までのUIフローを検証します。
一方、Apidogでは次のAPIチェーンを検証します。
- 支払い意図の作成
- 不正チェック
- 在庫減算
- 注文作成
- 決済失敗時のロールバック
支払いゲートウェイがレスポンスフィールドをerror_codeからerrorCodeに変えた場合、スキーマ検証で早期に検出できます。
チャートデータを含むSaaSダッシュボード
B2B分析サービスでは、Playwrightでダッシュボードの描画を確認します。
Apidogでは、集計APIが次を正しく返すか検証します。
- 合計値
- パーセンタイル
- 時間バケット
- フィルター条件
- 外れ値処理
チャートが見た目上は正常でも、APIの集計ロジックが間違っていれば、API層で検出できます。
Webhook駆動型ワークフロー
フィンテック系のチームでは、Playwrightでユーザーポータルを検証し、ApidogでWebhookを検証します。
検証対象は次のようなものです。
- Webhook署名
- リトライロジック
- 重複Webhook IDの拒否
- 冪等性
- 最終的整合性の時間制限
結論
Playwrightはブラウザフローの検証に優れています。しかし、深いAPIテストをすべてPlaywrightだけで管理するのは効率的ではありません。
PlaywrightとApidogを組み合わせると、次の構成を作れます。
- OpenAPI仕様を1つの契約として共有する
- PlaywrightでUIフローと薄いAPIチェックを行う
- Apidogでスキーマ、連鎖シナリオ、エラーパスを検証する
- 同じフィクスチャを両方のスイートで使う
- モックサーバーでフロントエンド開発を止めない
- CIでUI退行とAPI退行の両方を検出する
最初は、チェックアウトやサインアップのような重要フローを1つ選んでください。
-
openapi.yamlを用意する - PlaywrightのAPIフィクスチャを作る
- 対応するApidogシナリオを作る
- 同じフィクスチャJSONを共有する
- CIで両方を実行する
この小さな構成から始めれば、UIテストだけでは見逃すAPIバグを段階的に減らせます。
FAQ
ApidogなしでPlaywrightテストだけでAPIを検証できますか?
はい。Playwrightのrequestフィクスチャとexpectを使えば、ステータスコードや一部のボディフィールドは検証できます。
ただし、スキーマ検証、連鎖シナリオ、モック、エラーパスの網羅性を考えると、Apidogのような専用ツールを併用する方が管理しやすくなります。比較はQAエンジニア向けAPIテストツールも参考になります。
このセットアップにはOpenAPI仕様が必要ですか?
最大の効果を得るには必要です。
OpenAPI仕様がない場合でもPlaywrightとApidogを並行実行できますが、共通の情報源がなくなり、サンプルペイロードを複数箇所で管理することになります。
認証はどう扱えばよいですか?
テスト実行時に認証APIから新しいトークンを取得します。
Playwrightではフィクスチャに保存し、Apidogでは環境変数に保存します。固定トークンをハードコードすると、期限切れや権限変更で不安定になります。
ApidogはPlaywrightを置き換えられますか?
いいえ。
ApidogはAPIワークフローの検証に向いていますが、ブラウザをレンダリングしません。表示テキスト、レイアウト、クリックフロー、アクセシビリティなどのUI検証にはPlaywrightが必要です。
ステージング環境が安定していない場合はどうすればよいですか?
Apidogのモックサーバーを使います。
OpenAPI仕様からモックを起動し、Playwrightの接続先をモックサーバーに向けます。ステージングが復旧したら、Apidogシナリオを実バックエンドに対して実行します。
CIを高速に保つにはどうすればよいですか?
テストを優先度で分けます。
- push時:
@smoke - mainへのPR: 回帰テスト
- 夜間: 全Apidogシナリオ
Playwrightはworkersで並列化し、ApidogシナリオもCLIの並列実行を使います。
CIで有料のApidogプランが必要ですか?
利用条件はプランによって変わる可能性があります。導入前に現在の料金ページを確認してください。小規模チームでは無料枠で検証を始められる場合があります。
Top comments (0)