Playwright Test Agents入門:AIと分担するテストの計画・生成・修復
planner・generator・healerを使い、テスト計画から修正差分まで確認する

E2Eテストを増やそうとして、「何を確かめるかを決める」「画面に合うコードを書く」「画面変更で壊れたテストを直す」の間を何度も往復していませんか。
Playwright Test Agentsは、この3つの仕事をAIに分担させるための仕組みです。この記事では、テスト計画を確認してからコードを生成し、修復後は差分を確認する順番で使います。AIがテストを通したことと、必要な仕様を検証できたことを分けて判断するのがポイントです。
Playwright Test Agentsは何をするのか
Test Agentsには、画面を探索するplanner、計画からコードを作るgenerator、失敗を調べるhealerという3つの役割があります。AIツールが利用するエージェント定義と、ブラウザやテストを操作するMCPツールを組み合わせて動きます。MCPは、AIが外部の道具を呼び出すための接続方式です。Playwright公式:Test Agents
| 役割 | 渡すもの | 確認する成果物 |
|---|---|---|
| planner | 対象の操作、初期状態、期待する仕様 | 操作手順と期待結果を記したMarkdownの計画 |
| generator | 確認した計画、seed test | 実行できるテストコードと実行結果 |
| healer | 失敗したテスト、変えてはいけない期待値 | 原因、修正差分、再実行の結果 |
この表は、本記事で確認する工程の整理です。最初から3つをまとめて依頼するより、計画と修正の境目で成果物を読むと、意図と違うテストが増えるのを見つけやすくなります。
codegenやPlaywright MCPとの違い
Playwrightのcodegenは、人がブラウザを操作する様子を記録してコードを作る道具です。すでに操作の流れが決まっていて、ロケーターを含む下書きを素早く作りたい場面に使えます。Playwright公式:Test generator
Test Agentsは、何をテストするかという計画も成果物に含めます。一方、Playwright MCPはAIにブラウザ操作の道具を渡す接続です。この記事では「画面を操作する道具」と「計画・生成・修復の役割」を分けて理解します。通常のE2Eテストの書き方は、Playwrightの基礎から実践までで扱っています。
検証環境と今回の題材
検証日は2026年10月14日です。環境はWindows、Node.js 24.21.0、@playwright/test 1.64.0、Chromium 156.0.8078.4でした。この記事のコードは、Recurrent Hubのローカルの本番ビルドに対して実行したものを掲載します。
検証したAIツールはClaude Codeです。他の--loop、別OS、CI、認証付きの画面では実行していません。生成された定義の説明と、実際に試した結果は分けて記します。
題材はRecurrent Hubのローカルサイトです。トップページから記事を開き、記事のタイトルが表示されることを確かめます。ユーザーの操作と結果がつながる小さな流れなので、計画とコードの対応を追いやすい題材です。

本文の画像はローカルサイトの実際の画面を切り出したものです。表示用に縮小しています。新着記事一覧の撮影では、一覧に重なる固定ヘッダーを画面上で非表示にしました。
Claude Codeに導入する
Playwrightとブラウザを用意する
以下はNode.js・npmとClaude Codeを利用できる環境で行います。Claude Codeの導入そのものはこの記事の範囲に含めません。今回の検証では、専用のフォルダに@playwright/testを入れました。既存のWebアプリと別のフォルダで検証すれば、アプリの依存関係を変えずに試せます。
npm init -y npm i -D @playwright/test@1.64.0 npx playwright install chromium
続いてplaywright.config.tsを作ります。baseURLは検証対象のローカルサイトのURLです。今回は別のターミナルで本番ビルドしたサイトをポート3147で起動しました。読者のアプリでは、実際に使っているURLに合わせてください。
この後の依頼文・計画・コードはRecurrent Hubを題材にした実測例です。自分のアプリで試す場合は、クリックする要素、移動先URL、期待するタイトルを決めてplannerへ渡してください。掲載コードを直接使うなら、baseURLだけでなくhref、toHaveURL、toHaveTitle、見出しの取得方法も変更する必要があります。生成される計画やコードは毎回同じとは限りません。
import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests', reporter: [['list']], use: { baseURL: 'http://localhost:3147', trace: 'retain-on-failure', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, ], });
エージェント定義を追加する
npx playwright init-agents --loop=claude
設定ファイルを作った後に実行したところ、次のファイルが生成されました。表示の区切りを/に揃えています。
新しいフォルダでは、テスト計画用のspecs/README.mdも生成されます。今回は設定前の初回実行ですでに作成されていたため、設定後の再実行では作成ログに現れませんでした。
.claude/agents/playwright-test-planner.md .claude/agents/playwright-test-generator.md .claude/agents/playwright-test-healer.md .mcp.json tests/seed.spec.ts
今回のWindows環境では、.mcp.jsonの内容は次のとおりでした。これも生成されたものです。別のOSで生成される内容とは区別してください。
{ "mcpServers": { "playwright-test": { "command": "cmd", "args": [ "/c", "npx", "playwright", "run-test-mcp-server" ] } } }
--loopは使うAIツールを選ぶオプションです。今回実行したのはclaudeです。1.64.0のヘルプにはcodex、copilot、opencode、vscode、vscode-legacyも表示されましたが、それらの動作は今回は検証していません。
init-agentsは定義をプロジェクトに追加するコマンドです。実行しただけでテストの計画・生成・修復が始まるわけではありません。追加された定義をAIツールが読み込める状態にしてから、対象と成果物を指定して依頼します。
seed testで開始状態を用意する
seed testは、エージェントが探索を始めるための初期状態を用意するテストです。生成されたseedを、ローカルサイトのトップページを開く内容にしました。
import { test, expect } from '@playwright/test'; test.describe('Test group', () => { test('seed', async ({ page }) => { await page.goto('/'); }); });
expectはこのseedでは使っていません。生成された雛形のimportを残しています。認証が必要なアプリでは初期状態の作り方も変わりますが、認証の実践は今回の検証範囲に含めません。
つまずいたポイント:設定より先に初期化した
最初は設定ファイルのない状態でinit-agentsを実行し、seedがプロジェクト直下のseed.spec.tsに生成されました。その後、testDir: './tests'の設定を作って再実行すると、tests/seed.spec.tsが生成されました。
この記事の手順は、その経験を踏まえて設定を先に作る順番にしています。自分のプロジェクトでは、AIへ渡したseedのパスと実際のファイルが一致するかを確認してください。
plannerで計画を作り、期待結果を確認する
導入した検証プロジェクトでClaude Codeを起動し、対話画面に/mcpと入力してプロジェクトのplaywright-test MCPの接続を確認しました。接続画面そのものは保存していませんが、観察記録と、各エージェントがMCPツールを呼び出した実行記録を残しています。既存の別フォルダのセッションをそのまま使うのではなく、生成された定義と設定のあるフォルダで起動します。そのうえで、次のように依頼しました。
playwright-test-planner サブエージェントを使って、http://localhost:3147 で動いているローカルのRecurrent Hubについて、「トップページから新着記事のリンクを開き、記事ページへ移動する」導線のテスト計画を作り、specs/top-to-article.md に保存してください。seed は tests/seed.spec.ts です。対象はこの導線だけにし、ログイン・フォーム送信・外部サイトへの移動は試さないでください。
生成されたspecs/top-to-article.mdには13のシナリオがありました。先頭記事のクリックに加えて、戻る・進む、キーボード操作、モバイル幅、不正な記事URLなども提案されていました。これは今回生成された計画であり、Test Agentsが毎回同じ数のシナリオを作るという意味ではありません。
ここで、まず試す範囲を「1.3. 先頭の新着記事をクリックして記事ページへ遷移する」に絞りました。計画の該当部分は次のとおりです。
#### 1.3. 先頭の新着記事をクリックして記事ページへ遷移する(正常系) **File:** `tests/top-to-article/click-first-article.spec.ts` **Steps:** 1. 新着記事の先頭カード(npmとnpxの違い)をクリックする - expect: URLが http://localhost:3147/posts/nextjs/npm-vs-npx-guide になる - expect: document.titleが「【初心者向け】npmとnpxの違いとは?Windowsでnpxを使う方法と動かないときの対処も解説 | Recurrent Hub」になる - expect: h1が1つだけ存在し、カードのh3の「 - 」より前のタイトルと一致する - expect: ヘッダーとフッターが表示され、404やエラー表示でない
計画は探索時点の先頭記事を名前で記しています。新着記事は更新されるため、継続して使うテストでは「特定の記事を確認するのか」「表示中の先頭カードと移動先の対応を確認するのか」を決め直す必要があります。今回は探索時の先頭だったnpm・npx記事を開くテストとして検証します。
計画を読むときは、クリックする要素だけでなく、その先で何が起きれば成功なのかを確認します。今回なら「記事リンクをクリックできる」と「対応する記事ページへ移動し、タイトルが表示される」は別の確認です。
画面を探索して作った計画は、実装に存在する機能を拾う助けになります。ただし、画面で見えている挙動がそのまま正しい仕様とは限りません。要件書や受入条件があるなら、計画と照合してから次へ進みます。
generatorでコードを作り、通常のテストとして実行する
次の依頼で、計画の1.3だけをコードにしました。
playwright-test-generator サブエージェントを使って、specs/top-to-article.md の「1.3. 先頭の新着記事をクリックして記事ページへ遷移する(正常系)」だけをテストコードにしてください。保存先は計画どおり tests/top-to-article/click-first-article.spec.ts です。ほかのシナリオは作らないでください。
生成されたコードは次のとおりです。
// spec: specs/top-to-article.md // seed: tests/seed.spec.ts import { test, expect } from '@playwright/test'; test.describe('新着記事から記事ページへの遷移', () => { test('先頭の新着記事をクリックして記事ページへ遷移する(正常系)', async ({ page }) => { await page.goto('/'); // 1. 新着記事の先頭カード(npmとnpxの違い)をクリックする const card = page.locator('main a[href="/posts/nextjs/npm-vs-npx-guide"]'); await expect(card).toBeVisible(); const cardHeading = (await card.getByRole('heading', { level: 3 }).textContent()) ?? ''; const expectedTitle = cardHeading.split(' - ')[0].trim(); await card.click(); // expect: URL await expect(page).toHaveURL('http://localhost:3147/posts/nextjs/npm-vs-npx-guide'); // expect: document.title await expect(page).toHaveTitle( '【初心者向け】npmとnpxの違いとは?Windowsでnpxを使う方法と動かないときの対処も解説 | Recurrent Hub' ); // expect: h1 is unique and matches card title const h1 = page.getByRole('heading', { level: 1 }); await expect(h1).toHaveCount(1); await expect(h1).toHaveText(expectedTitle); // expect: header and footer visible, no error await expect(page.getByRole('banner').first()).toBeVisible(); await expect(page.getByRole('contentinfo')).toBeVisible(); await expect(page.getByText('404')).toHaveCount(0); }); });
通常のPlaywright Testとして、次のコマンドで実行しました。
npx playwright test tests/top-to-article/click-first-article.spec.ts
筆者が再実行した結果の抜粋です。
Running 1 test using 1 worker 1 passed (6.1s)
実行時間はこの環境での1回の結果です。速度改善や成功率を測定したものではありません。
このコードは、検証時に先頭だったnpm・npx記事をURLで指定しています。新しい記事が追加されても常に先頭を選ぶテストにはなっていません。また、最後のgetByText('404')は、記事本文にその文字が現れた場合にも失敗する可能性があります。今回の対象記事では検証できたコードですが、別のページへ展開するときは、計画の意味とロケーターの条件を見直してください。
計画の「エラー表示でない」について、生成コードが否定確認しているのは「404」という文字列までです。一般的なエラー表示、HTTPステータス、コンソールエラーは検査していません。必要なら、対象アプリのエラー表示の仕様を決め、確認項目を追加します。

コードを読むときは、計画の期待結果がexpectに対応しているかを確かめます。「リンクをクリックした」で終わるテストと、「移動先とタイトルを確認した」テストでは、検証している範囲が違います。
また、カードから読み取ったタイトルと移動先のタイトルを比較するテストは、両者の対応を確かめるものです。両方が同じ誤ったデータから表示されていれば、その誤りを見逃す可能性があります。商品名や価格など業務上の正しさを確かめるなら、仕様やテストデータに基づく独立した期待値も必要です。
Playwrightのexpect(locator)を使ったアサーションには、条件が満たされるまで再確認するものがあります。画面の更新待ちを、固定秒数の待機で埋める前に、何の状態を待つべきかを考えます。Playwright公式:Assertions
healerで失敗を調べ、修正前後を比較する
healerの動きを確かめるため、正常なテストのロケーターに、実際のリンクには存在しない.post-cardを加えました。アプリ側は変更していません。
- const card = page.locator('main a[href="/posts/nextjs/npm-vs-npx-guide"]'); + const card = page.locator('main a.post-card[href="/posts/nextjs/npm-vs-npx-guide"]');
同じテストを実行すると、要素が見つからず失敗しました。ログの抜粋です。
Error: expect(locator).toBeVisible() failed Locator: locator('main a.post-card[href="/posts/nextjs/npm-vs-npx-guide"]') Expected: visible Timeout: 5000ms Error: element(s) not found
そこで、次の依頼をしました。
tests/top-to-article/click-first-article.spec.ts が失敗するようになりました。playwright-test-healer サブエージェントを使って、原因を調べて直してください。期待値を弱めたり、skip・fixme・timeout の延長で通したりはしないでください。
healerは、追加したクラス指定を取り除く1行の修正を行いました。
- const card = page.locator('main a.post-card[href="/posts/nextjs/npm-vs-npx-guide"]'); + const card = page.locator('main a[href="/posts/nextjs/npm-vs-npx-guide"]');
修復されたテストを筆者が同じコマンドで再実行した結果です。
Running 1 test using 1 worker 1 passed (2.6s)
生成した正常版と修復後のコードを比較すると、元のロケーターに戻っていました。URL、ページタイトル、h1、ヘッダー・フッターの確認は残っており、skip・fixmeの追加やtimeoutの延長はありませんでした。
修復が正しくても、原因の説明は確かめる
今回、healerの報告は「アプリ側でリンクからpost-cardというクラス名がなくなった」という説明でした。しかし実際には、筆者がテストへ存在しないクラス名を加えています。アプリからクラス名を削除した事実はありません。
ロケーターの修復は意図どおりでも、変更の経緯についての説明は実験の事実と一致しませんでした。AIの報告をそのまま障害原因の記録にせず、コードの差分とアプリの変更履歴を確かめる必要があります。今回の結果は、この1つの故障を使った検証であり、healerの修復全般の成功率を示すものではありません。
healerに渡すときは、「テストを通して」だけでなく、保持する期待結果を書きます。今回の検証では、期待値を弱めず、skip・fixmeやtimeoutの延長を使わない条件で依頼しました。
修正後に見るのは、結果の緑色だけではありません。対象の要素を正しく探すようになったのか、確認すべき条件を外してしまったのかを差分で読みます。
スキップされたテストも確認する
今回生成されたplaywright-test-healer.mdを読むと、テストが正しいと判断しても失敗が続く場合に、test.fixme()で実行対象から外す指示が含まれていました。これは定義を読んで確認した内容です。今回、その分岐を実行させたわけではありません。
自動修復を使うなら、passedの数と合わせてskippedの数も確認してください。必要な機能がテストされなくなった場合は、原因を別の課題として追う必要があります。
使う前に決めておきたいこと
以下は今回の実践から、チームへ導入するときに勧めたい運用です。導入先のCIや認証付きアプリでは未検証です。
| 決めること | 具体例 |
|---|---|
| 対象を小さくする | トップページから記事を開く1つの流れ |
| 期待値を先に書く | 移動先のURLと記事タイトルを確認する |
| 修復の境界を決める | ロケーター修正は検討する。期待値の削除は認めない |
| 止める条件を決める | 同じ失敗が続いたら原因と未解決点を報告する |
| 成果物を残す | 計画、テスト、失敗ログ、修正差分を対応づける |
使いやすいのは、画面操作の流れは分かるものの、テスト計画とコードを一緒に整えたい場面です。期待仕様が曖昧なままの機能では、先に何を合格とするかを決める必要があります。
画面の見た目を画像で比較するテストでは、差分が意図した変更かどうかの判断も加わります。healerに任せる範囲を広げる前に、Playwrightのビジュアルリグレッションテストでベースラインの扱いを確認してください。
まず1つの操作で始める
Test Agentsを試すときは、対象の操作を1つ決め、plannerの計画を読み、generatorのコードを実行し、healerの修正を差分で確認するところまで進めてみてください。
計画とコードと修正理由が残れば、AIが作ったものを次回のテスト追加にも使えます。テストの本数だけでなく、必要な仕様を説明できるかを判断の基準にすると、AIと分担する範囲を決めやすくなります。