こんにちは、関根です。
今回は、Playwright MCPとPlaywright Testを使って、自己完結型のシンプルなTodoアプリを題材にしたハンズオンをお届けします。自然言語の指示だけでブラウザを操作するデモから、同じ操作をコード化してヘッドレスで自動実行するところまで、実際に手を動かしながら体験していきます。
所要時間の目安は20〜30分程度です。ぜひ手元の環境でこの記事の通りに進めてみてください。
前提・事前準備事項
- 手元の環境にNode.js 18以上がインストールされていること (node –versionで確認できます)
- Claude Code(CLI)がインストールされていること
- Playwright MCPはハンズオン中に案内されます(インストール後、Claudeの再起動が必要になります)
- Playwright Testはハンズオン中に案内されます
事前準備
今回のハンズオンで使う進行ルール・仕様書は、すべてMarkdownファイルとして用意しています。
それぞれのファイルの役割は次のとおりです。
| ファイル | 説明 |
|---|---|
CLAUDE.md |
ハンズオン全体の進行ルール |
PW-01_Todo画面生成.md |
STEP1: Todoアプリの仕様書 |
PW-02_操作デモ.md |
STEP2: 操作デモの進め方 |
PW-03_自動テスト.md |
STEP3: 自動テストの実行手順 |
PW-テストケース.md |
STEP2・STEP3共通のテストケース一覧 |
ハンズオンで得られるもの
- Playwright MCPで、自然言語の指示からブラウザ操作を組み立てる感覚
- Playwright Testで、同じ操作をテストコードとして再現し、HTMLレポートで合否を確認する書き方
MCP・Playwright MCP・Playwright Testについて
MCP(Model Context Protocol)自体の解説記事は他にもたくさんあるので、ここでは深くは触れません。ざっくり言うと、AIアシスタントが外部のツールを呼び出すための共通規格です。
今回登場するPlaywright MCPとPlaywright Testは、どちらもMicrosoftが開発するブラウザ自動化ライブラリPlaywrightを土台にしていますが、役割は次のように異なります。
| Playwright MCP | Playwright Test | |
|---|---|---|
| 役割 | 自然言語の指示をブラウザ操作に変換して実行 | 操作をテストコードとして記述・実行 |
| 使う場面 | 操作デモ、その場での確認(STEP2) | 再現可能な自動テスト(STEP3) |
| 結果の残り方 | その場のスナップショット・スクリーンショット | HTMLレポートとして自動保存 |
余談ですが、Playwright MCPは自然言語の指示でブラウザ操作を組み立てられる特性上、従来RPAツールが担っていたような定型的なブラウザ操作の自動化という使い方もできます。
ハンズオンの開始
ダウンロードしたファイルを作業ディレクトリに置いたら、あとは次のように声をかけるだけでハンズオンが始まります。
@CLAUDE.mdの内容に沿って、このハンズオンを進めて
CLAUDE.mdに書かれた進行ルールに従って、各STEPの着手前に「STEP◯を始めてよいか」を確認しながら進めてくれます。
STEP1: Todoアプリ画面の生成
自動テストの練習をするとき、いきなり本番環境や既存の業務アプリを対象にするのは危険です。誤ってデータを消してしまったり、意図しない操作で状態を壊してしまったりするおそれがあるためです。そこでこのハンズオンでは、テスト対象専用のシンプルなTodoアプリを自分たちで用意するところから始めます。
まず、STEP2・STEP3のテスト対象となるTodoアプリを生成しました。外部ライブラリ等は一切使わず、HTML・CSS・素のJavaScriptのみの単一ファイルで構成しています。
- タスクの入力欄と追加ボタン
- 追加したタスクの一覧表示
- 各タスクのチェックボックス(完了/未完了の切り替え)と削除ボタン
サーバー側はNode標準のhttpモジュールのみで実装したごく簡単な静的配信サーバーで、ポートは4175を使用します。
node server.jsを実行し、ブラウザでlocalhost:4175を開くと、タスク0件の初期画面が表示されることを確認できます。
STEP2: Playwright MCPで画面を見ながらテスト(操作デモ)
ここでPlaywright MCPが設定されます(インストール・設定されない場合は、トラブルシューティングをご参照ください)。インストール後に再起動を促すメッセージが表示された場合は、指示に従ってClaudeを再起動してください。
次に、テストコードを書く前段階として、Playwright MCPを使い、自然言語の指示だけでブラウザが実際に動く様子を体験します。
| # | ケース内容 | 渡した自然言語の指示 |
|---|---|---|
| 1 | アプリを開いてタイトル・見出しを確認する | アプリを開いて、タイトルと見出しを確認して |
| 2 | タスクを1件追加する | 入力欄に「牛乳を買う」と入力して、追加ボタンをクリックして |
| 3 | Enterキーでタスクを追加する | 入力欄に「レポートを提出する」と入力して、Enterキーで追加して |
| 4 | タスクを完了にする | 「牛乳を買う」のチェックボックスをクリックして完了にして |
| 5 | タスクを複数件追加して件数を確認する | 「掃除機をかける」「本を返却する」「メールを返信する」を追加して、件数を確認して |
| 6 | タスクを1件削除する | 「レポートを提出する」の削除ボタンをクリックして削除して |
| 7 | 現在の画面のスクリーンショットを撮る | 今の画面全体のスクリーンショットを撮って |
| 8 | タスクをすべて削除する一括操作 | 残っているタスクをすべて削除して |
実際には、この「渡した自然言語の指示」の列のような文章を、そのままプロンプトとして渡すだけです。ここでは、そのうちケース1・2・4について、渡したプロンプトと実行結果を見ていきます。
ケース1: アプリを開いてタイトル・見出しを確認する
Playwright MCPを使って、アプリを開き、タイトルと見出しを確認して
このプロンプトを入力すると、ブラウザが起動し、Todoアプリの画面が自動で開きます。ページタイトルが「Todoアプリ」になっていること、見出し(h1)にも「Todoアプリ」と表示されていることが、その場で確認できます。タスクはまだ0件のまっさらな状態です。
ケース2: タスクを1件追加する
Playwright MCPを使って、入力欄に「牛乳を買う」と入力し、追加ボタンをクリックして
このプロンプトを入力すると、入力欄に自動で「牛乳を買う」と入力され、続けて追加ボタンがクリックされます。一覧に「牛乳を買う」というタスクが1件、その場で表示されます。
ケース4: タスクを完了にする
Playwright MCPを使って、「牛乳を買う」のチェックボックスをクリックして完了にして
このプロンプトを入力すると、「牛乳を買う」のチェックボックスが自動でクリックされます。チェック済みになり、テキストに打ち消し線が付いて、完了状態が視覚的に区別できるようになります。
おまけ1: もう少し長い操作もやらせてみる
Playwright MCPを使って、Todoアプリを開き、「資料を作成する」「会議に参加する」
「経費精算をする」の3つのタスクを追加して、すべてにチェックを入れてから、
すべて削除して
1件ずつではなく、こうした一連の操作もひとつのプロンプトから実行できます。このプロンプトを入力すると、アプリを開く→3件のタスクを追加する→すべてにチェックを入れる→すべて削除する、という流れが自動で進みます。
おまけ2: 画面から仕様書を逆生成してみる
Playwright MCPを使って、Todoアプリの画面を開き、見た目や操作をもとに
仕様書をMarkdownで書き起こして
このプロンプトを入力すると、画面のDOM構造やdata-testid、操作したときの挙動から、次のような仕様書が書き起こされました。
## 要素とセレクタ
| 要素 | data-testid |
|---|---|
| タスク入力欄 | new-todo-input |
| 追加ボタン | add-todo-button |
| タスク項目(1件ごと) | todo-item |
| 完了チェックボックス | todo-checkbox |
| 削除ボタン | delete-todo-button |
## 挙動
・入力欄にテキストを入力し、追加ボタンをクリックするとタスクが一覧に追加される
・入力欄はフォームの一部になっており、Enterキーでも同様に追加される
・チェックボックスをクリックすると完了状態になり、completedクラスが付与される
・ページを再読み込みすると一覧は空に戻る(永続化されていない)
トラブルシューティング: Playwright MCPが使われない場合
プロンプトを渡しても、Playwright MCP以外の方法でブラウザ操作が実行されてしまうことがあります。その場合は、次のようにはっきり指摘してください。
Playwright MCPが使用されていません!Playwright MCPを使ってやり直して
以下のような案内がでますので追加方法を選択してください。
[ ] 追加方法
Playwright MCPサーバーをどの方法で追加しますか?
> 1. claude mcp addでプロジェクトに追加(推奨)
npx @playwright/mcp@latest を scope=project で登録。
.mcp.jsonに設定が保存され、このプロジェクトを開く人全員が利用できる
2. claude mcp addで自分の環境のみに追加
scope=local(自分の環境のみ、他の人には共有されない)で登録
3. インストールをやめてclaude-in-chromeを使う
MCP追加はせず、現在利用可能なclaude-in-chromeツールでSTEP2を進める
4. Type something.
──────────────────────
5. Chat about this
それでも改善しない場合は、一度/clearで会話をリセットしてから、同じプロンプトを実行し直してみてください。
なお、STEP2のMCP操作の結果はtest-results/やHTMLレポートには自動反映されません。「言葉で動く」ことは体感できましたが、これが再現可能なテストとして固定されているかどうかの最終確認は、次のSTEP3で行います。
STEP3: ヘッドレスでの自動テスト一括実行
STEP2と同じ操作がコードとして再現可能であることを確認するため、tests/todo.spec.jsにケース1〜7に対応するテストコードを用意し、画面表示なし(ヘッドレス)で一括実行しました。ケース8はテストコードに含めず、参加者演習として自分でコード化する課題としています。
cd playwright-demo
npm install
npx playwright install chromium
npx playwright test
実行の結果、7件すべてのテストが成功しました(4.3秒で完走)。
Running 7 tests using 2 workers
...
7 passed (4.3s)
次のコマンドでHTMLレポートを開くと、各テストの合否・実行時間に加えて、成功・失敗を問わず自動保存されたスクリーンショットが確認できます。
cd playwright-demo
npx playwright show-report
各テストの詳細画面を開くと、ステップごとの実行内容とスクリーンショットが埋め込まれており、失敗時にはトレース(操作の再生)も確認できるようになっています。
まとめ
今回のハンズオンでは、STEP1で仕様書からアプリを生成し、STEP2でPlaywright MCPによる自然言語の操作デモを、STEP3でPlaywright Testによるコード化・自動実行を、それぞれ体験しました。同じ操作対象(自己完結型のTodoアプリ)に対して「言葉で動かす」体験と「コードとして再現する」体験を対比できる構成になっています。
STEP2では、1件ずつの操作だけでなく、複数の操作をまとめた一連の流れをひとつのプロンプトから実行したり、逆に動いている画面から仕様書を書き起こしたりする「おまけ」も試しました。定型的なブラウザ操作をまとめて任せられる、仕様書が残っていない既存画面からドキュメントを起こせる、といった点は、RPAツールが担ってきたような実務寄りの使い道にもつながりそうです。
一方で、STEP2の結果はそのままでは合否判定や継続的な検証には使えません。STEP3のようにテストコードとして固定し、HTMLレポートで結果を残せるようにして初めて、再現性のある自動テストとして運用できます。デモとしてのPlaywright MCP、検証としてのPlaywright Testという役割の違いを意識しておくと、使い分けやすいはずです。
付録: ケース8の解答例
ケース8「タスクをすべて削除する一括操作」は、STEP3のテストコードには含めず、STEP2の自然言語指示→STEP3のコード化という順番で自分で書いてみる課題として残していました。渡すプロンプトはSTEP2のケース一覧に載せたとおりです。
残っているタスクをすべて削除して
これをテストコードにすると、次のようになります。
test('STEP8: タスクをすべて削除する一括操作', async ({ page }) => {
const tasks = ['牛乳を買う', 'レポートを提出する', '掃除機をかける'];
for (const task of tasks) {
await page.getByTestId('new-todo-input').fill(task);
await page.getByTestId('add-todo-button').click();
}
const deleteButtons = page.getByTestId('delete-todo-button');
while (await deleteButtons.count() > 0) {
await deleteButtons.first().click();
}
await expect(page.getByTestId('todo-item')).toHaveCount(0);
});
削除するたびに一覧が再描画されて件数が変わっていくので、決め打ちのループ回数ではなく、削除ボタンが0件になるまで先頭から削除し続ける書き方にしています。
もちろん、この解答例自体もAIに生成させることができます。「STEP2のケース8をPlaywright Testのテストコードにして」と指示するだけで、同じような実装を提案してくれるはずです。自分で書いてみたコードとAIが生成したコードを見比べてみるのも、理解を深める良い練習になります。





