はじめに
AIの登場で開発スピードは大きく上がりました。
一方で、AIが書いたコードの品質までは保証してくれません。
そのため、QA(品質保証)の役割はむしろ大きくなっていますが、
すべてを手動でテストするのは現実的でないです。
そこで今回は、AIエージェントを使ってE2Eテストをどこまで自動化できるのかを検証してみました。
この記事は導入編です。Playwright Agents を Next.js(App Router)のプロジェクトに入れて、エージェントが動ける状態にするまでをまとめています。
実際にテストを計画・生成・修正させた結果は、実践編として別の記事にまとめる予定です。
Playwright Agentsとは
2025年10月(Playwright v1.56)に登場した E2E テスト(エンドツーエンドテスト)を自動化する機能です。
従来、E2E テストの作成には以下のような課題がありました:
- テストケースの洗い出しに時間がかかる
- テストコードの実装が難しく、手間
- UI 変更のたびにテストを書き直す
Playwright Agents では、これらの課題を3つのAIエージェントが分担して解決します。テストの計画から実装、修正までをAIに任せることで、
開発者は「どうやってプロダクトを伸ばすか、どういう機能を作るべきか」など
本来エンジニアとして1番大事な部分に集中できます。
前提として
- Next.js(App Router)
- Playwright v1.57.0
- Claude Code から使う
- ハーネスエンジニアリングの設計で、
.claude/配下にagents/(サブエージェント)やrules/を置いている
ハーネスや .claude/agents が分からなかったら以下の記事を読んでください。
「AIが書いたから大丈夫」が一番危ない😱 ハーネスエンジニアリング完全ガイド
導入で使ったPR
3つのエージェントの概念
Planner(計画)
アプリを実際に操作しながら画面を探索し、テスト計画をMarkdownで作成します。計画は specs/ 配下に出力されます。
Generator(実装)
Plannerが作った計画をもとに、実際にブラウザでセレクタや動作を確認しながらPlaywrightのテストコードを生成します。
Healer(修正)
失敗したテストを再実行してUIを調べ、セレクタの変更などに合わせてテストを修正します。
3つのエージェントの違い
定義ファイルを見ると、使えるツールが違います。
| エージェント | ファイルの書き方 | seed を実行するか |
|---|---|---|
| Planner |
planner_save_plan(MCP)で計画を保存 |
する(planner_setup_page) |
| Generator |
generator_write_test(MCP)でテストを保存 |
する(generator_setup_page) |
| Healer |
Edit / Write で直接テストを書き換える |
しない(test_run で既存テストを実行) |
ファイルを直接書き換えられるのは Healer だけです。後で説明しますが、ここがカスタマイズのポイントになります。
このPJの構成
init-agents 実行後、Playwright Agents 関連のファイルはこうなります。
.
├── .claude/
│ ├── agents/
│ │ ├── playwright-test-planner.md # 追加(init-agents)
│ │ ├── playwright-test-generator.md # 追加(init-agents)
│ │ ├── playwright-test-healer.md # 追加(init-agents)
│ │ └── code-review.md など # 既存のサブエージェント
│ └── rules/
│ ├── testing-unit.md # testing.md を分割
│ └── testing-e2e.md # testing.md を分割
├── .mcp.json # 追加(playwright-test MCP サーバー)
├── e2e/
│ ├── helpers/auth.ts # 既存(createTestUser / signup など)
│ ├── global-teardown.ts # 既存(テストユーザーの削除)
│ ├── seed.spec.ts # 追加(init-agents)
│ └── *.spec.ts # 既存のE2Eテスト
├── specs/
│ └── README.md # 追加(テスト計画の置き場)
└── playwright.config.ts # testDir: './e2e'
init-agentsを実行
Claude Code 向けに初期化します。
npx playwright init-agents --loop=claude
するとこんな感じで出てきます。
npm notice run next-app-router-share-app@0.1.0 npx
npm notice run 'playwright' init-agents --loop=claude
🎭 Using project "" as a primary project
📝 specs/README.md - directory for test plans
🌱 e2e/seed.spec.ts - default environment seed file
🤖 .claude/agents/playwright-test-generator.md - agent definition
🤖 .claude/agents/playwright-test-healer.md - agent definition
🤖 .claude/agents/playwright-test-planner.md - agent definition
🔧 .mcp.json - mcp configuration
✅ Done.
playwright.config.ts の testDir(このPJでは ./e2e)を読んで、seed ファイルを e2e/ に置いてくれます。
.mcp.json にはこれが追加されます。エージェントはこの MCP サーバー経由でブラウザを操作します。
{
"mcpServers": {
"playwright-test": {
"command": "npx",
"args": ["playwright", "run-test-mcp-server"]
}
}
}
Claude Code を再起動
起動時に playwright-test MCP サーバーの承認を求められるので、承認します。
seed を書き換える
seed は、Planner と Generator が作業を始める前に実行されるテストです。ログインなど、毎回必要な準備をここに書きます。
このPJでは、新規ユーザーを登録してログイン済みの状態にしました。
import { test } from '@playwright/test';
import { createTestUser, signup } from './helpers/auth';
test.describe('Test group', () => {
test('seed', async ({ page }) => {
// Playwright Agents が作業を始める前に、ログイン済みの状態を用意する
// このログイン状態は後続のテストには引き継がれないため、
// 生成するテストでも各テストの冒頭で createTestUser() + signup() を行う
const user = createTestUser();
await signup(page, user);
});
});
ポイントは3つです。
-
login()ではなくsignup()を使う:createTestUser()はまだ登録されていないユーザーを作るので、login()ではログインできません -
メールアドレスは
test-始まり:e2e/global-teardown.tsがテスト後にtest-始まりのユーザーを削除するので、DB にゴミが残りません -
seed のログイン状態は後続のテストに引き継がれない:生成されるテストでも、各テストの冒頭で
createTestUser()+signup()を行う必要があります
storageState でログイン状態を共有する方法もありますが、今回は不採用にしました。既存の auth.spec.ts が「未ログイン」を前提にしているためです。実践では、未ログインから始めようと思います。
以下のコマンドで seed を実行します。DBとdevサーバーの起動も忘れずに(playwright.config.ts に webServer を設定していないため、自分で起動します)。
# DB だけ起動
docker compose up db -d
# 別ターミナルで dev サーバーを起動
npm run dev
# seed を実行
npx playwright test e2e/seed.spec.ts
実行後、レポートで動いたか確認します。
npx playwright show-report
seed が通るとこんな感じの画面になります。
つまずいたところ
seed を通すまでに、いくつかハマりました。
| 症状 | 原因 | 対処 |
|---|---|---|
ERR_CONNECTION_REFUSED |
dev サーバーが起動していない | npm run dev |
waitForURL が10秒でタイムアウト |
DB が起動していない / 空で、signup が失敗している | DB を起動する。タイムアウトを延ばしても直らない |
| 3000番ポートが衝突 |
docker compose up -d で app サービスも起動していた |
docker compose up db -d で DB だけ起動 |
| signup が失敗する | DB にテーブルがない | npx prisma migrate dev |
特に2つ目は、タイムアウトに見えて実は signup の失敗なのが気がつかなかったです。
生成されたエージェント定義をそのまま使わない
init-agents が生成したエージェント定義を、CodeRabbitによるPRレビューで3か所修正しました。
① Healer:test.fixme() でスキップしない
生成された Healer には、こう書かれていました。
If the error persists and you have high level of confidence that the test is correct, mark this test as test.fixme() so that it is skipped during the execution.
テストが正しいのに失敗し続けるなら、それはアプリの不具合です。スキップするとバグが隠れてしまいます。
Healer はファイルを直接書き換えられるので、ここは特に危ないです。
そこで、テストは変えずに失敗内容を報告させるようにしました。
-- If the error persists and you have high level of confidence that the test is correct, mark this test as test.fixme()
- so that it is skipped during the execution. Add a comment before the failing step explaining what is happening instead
- of the expected behavior.
+- If the error persists and you have high level of confidence that the test is correct (the failure is caused by a bug
+ in the application), do not modify the test. Never mark it as test.fixme(), test.skip(), or change its assertions to
+ match the buggy behavior. Instead, report the failing test, the failing step, the expected behavior, the actual
+ behavior, and the evidence (error message, page snapshot) so that the application can be fixed.
② Generator:生成例の構文ミス
生成例のコードが不正な構文になっていました。お手本が間違っていると、生成されるテストも間違えるので直します。
- test('Add Valid Todo', async { page } => {
+ test('Add Valid Todo', async ({ page }) => {
③ Planner / Generator:seed ファイルを明示する
planner_setup_page / generator_setup_page に e2e/seed.spec.ts を渡すよう、定義に書き足しました。
ルールも整備する
.claude/rules/testing.md を、Unit と E2E で分けました。paths を指定しておくと、該当ファイルを触るときだけ読み込まれます。
-
testing-unit.md(paths: src/**/*.spec.{ts,tsx}) -
testing-e2e.md(paths: e2e/**,specs/**)
testing-e2e.md には、seed の前提やテストユーザーの作り方を書いています。
## Playwright Agents
- seed は `e2e/seed.spec.ts`(新規登録してログイン済みの状態にする)
- seed のログイン状態は後続のテストに引き継がれない。ログインが必要なテストは、各テストの冒頭で `createTestUser()` + `signup()` を行う
- テスト計画は `specs/`、生成したテストは `e2e/` に置く
なぜ Claude Code にしたか
init-agents は --loop で使うツールを選べます。VS Code(GitHub Copilot)向けの --loop=vscode もあり、こちらは .github/ 配下と .vscode/mcp.json が生成されます。
今回は、すでにハーネスを .claude/ に整備していて、Copilot は有料プランが必要になりやすいので、Claude Code(--loop=claude)にしました。
まとめ
-
npx playwright init-agents --loop=claudeだけで、3つのエージェントと MCP サーバーの設定が入る - seed に「ログイン済みにする」準備を書いておくと、Planner / Generator がそこから作業を始められる
-
生成されたエージェント定義はそのまま使わない。特に Healer の
test.fixme()はアプリのバグを隠すので外す
次の実践編では、実際に Planner → Generator → Healer を動かして、どこまで任せられたかをまとめます。
資料
使用したAI
最初自分で書いて、AIに添削をもらいました。
Claude Code
ChatGPT

