QR Send

スマホとPCのChrome間で、URLやテキストをQRコード1回のペアリングで送り合える自作Chrome拡張。アカウント不要・無料。

使ってみる →

自己修復(Healer)を試す:壊れたテストをAIが直す

Selenium・自動化

ボタンの表示が変わっただけでテストが真っ赤になった

ログインボタンのラベルが「ログイン」から「サインイン」に変わっただけで、テストが軒並み落ちる。Playwrightでロケータを書いていれば、誰もが一度は経験することだと思います。

私もPlaywright歴はまだ1年ちょっとですが、この手の修正で地味に時間を取られています。原因は明らかで直し方も単純なのですが、件数が多いと単純作業がひたすら続くのがつらいところです。

これまでの対処法は、落ちたテストを1件ずつ開いて、エラーメッセージを見て、該当のロケータをブラウザの開発者ツールで探し直す、という地道な作業でした。件数が10件くらいなら苦になりませんが、50件を超えてくると心が折れます(さすがに全部を手作業でやるのは無理があると割り切っています)。

そんな悩みに刺さりそうな機能が、Playwrightの公式ドキュメントに載っている「テストエージェント」です。中でも壊れたテストの修復を担当するHealerというエージェントを、実際に触りながら紹介します。

PlannerとGeneratorとHealer、3人組の役割分担

Playwrightのテストエージェントは1つではなく、Planner・Generator・Healerの3つが役割分担する構成になっています。というわけで、まずこの3つの関係を整理しておきます。

エージェント 入力 出力 役割
Planner やりたいことの指示・既存テスト Markdownのテスト計画 アプリを探索して計画を立てる
Generator Markdownの計画 実行可能なテストコード 計画をコードに落とす
Healer 失敗したテスト名 修復パッチ or スキップ提案 落ちたテストを直す

Planner→Generatorでテストを作り、後になってHealerが直す。この3つは1本の生産ラインになっているわけです。

簡単に言うと、Plannerは「アプリを実際に触りながら、何をテストすべきかのMarkdown計画を作る係」、Generatorは「その計画を実行可能なPlaywright Testのコードに落とす係」です。どちらも人間がレビューする前提で、計画は「この計画で合っているか」、コードは「この実装は安全か」を確認してから採用する、という運用になっています。

PlannerとGeneratorだけでも便利そうですが、今回は保守フェーズで一番出番が多そうなHealerに絞って触ってみます。テストを書くのは最初の1回だけですが、直すのはその後ずっと続くので、個人的にはHealerの方が普段使いになりそうだと感じています。

Healerを触る準備をする

テストエージェントはPlaywright本体に組み込まれているので、追加のパッケージは不要です。まずはエージェント定義をリポジトリに作ります。

npx playwright init-agents --loop=claude

--loopには自分が使っているツールに合わせてvscodeclaudecodexopencodeのいずれかを指定します。実行するとリポジトリにファイルが追加・変更されるので、クリーンなブランチで実行して差分をレビューしてからコミットするのが安全です。

公式でも「Playwrightをアップデートするたびに再実行してください」と案内されています。エージェント定義は最新の挙動に合わせて生成し直すもの、と覚えておけばよさそうです。

Healerがトレースを見て診断するので、playwright.config.tsでトレースを残す設定もしておきます。

export default defineConfig({
  use: {
    trace: 'retain-on-failure',
  },
});

init-agentsを実行すると、計画を置くspecs/ディレクトリと、生成済みテストを置くtests/ディレクトリの下地ができます。tests/seed.spec.tsという環境セットアップ用のテストも一緒に作られるので、いきなり中身を書き換えずに、まずは構造だけ眺めてみるのがおすすめです。

失敗したテストのトレースは、コマンドラインからも中身を覗けます。

npx playwright trace open test-results/login-chromium/trace.zip

Healerに直してもらう前に、まず自分でトレースを開いてどこで落ちているか確認する癖をつけておくと、提案されたパッチが妥当かどうかの判断がしやすくなります。

わざと壊してみる

百聞は一見にしかずなので、実際にテストを壊してHealerに直してもらう流れを試してみます。想定は、ログインフォームのボタンラベルを「ログイン」から「サインイン」に変えた、という例です(特定のプロジェクトの話ではなく、説明用の一般的な例として書いています)。

import { test, expect } from '@playwright/test';

test('ログインできる', async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.getByLabel('メールアドレス').fill('user@example.com');
  await page.getByLabel('パスワード').fill('password123');
  await page.getByRole('button', { name: 'ログイン' }).click();
  await expect(page.getByText('ようこそ')).toBeVisible();
});

ボタンのラベルが変わったので、getByRole('button', { name: 'ログイン' })が見つからずテストは落ちます。ここでいつもなら自分でロケータを直しに行くところですが、代わりにHealerへ頼んでみます。

Healerに直してもらう

Healerは自然言語で指示します。ホスト(VS CodeやClaude Codeなど)を通じて、こんな感じでお願いします。

Healerエージェントを使って、失敗しているテスト「ログインできる」を調査してください。
「メールアドレスとパスワードでログインできること」というテストの意図は変えないでください。

指示を受けたHealerは、失敗したステップを再実行し、実際のUIを検査して同等の要素を探します。今回のケースなら「ボタンのラベルがサインインに変わっている」ことに気づき、ロケータを更新するパッチを提案してくれる、という流れです。

提案されるのはロケータの更新だけではありません。公式ドキュメントによると、待機時間の調整やテストデータの修復(期限切れのクーポンコードを有効なものに差し替える、など)もHealerの守備範囲だそうです。直してそのまま再実行し、通ればそこで完了です。

提案されるパッチのイメージは、こんな感じの差分です。

- await page.getByRole('button', { name: 'ログイン' }).click();
+ await page.getByRole('button', { name: 'サインイン' }).click();

差分自体はロケータ1行の書き換えだけなので、地味と言えば地味です。ただ、自分でDOMを調べて該当箇所を特定する時間を考えると、これ、意外と使えます。

ここまで聞くと「もうロケータのメンテナンスから解放されるのでは」と期待したくなりますが、そこまで甘くはありませんでした。

そもそも、テストが落ちた原因はロケータの変更だけとは限りません。公式ドキュメントでは、Healerに提案させる前に落ちた原因を分類しておくべきだと案内されています。

落ちた原因 本来やるべき対応 Healerに直させてよいか
プロダクト側のバグ(回帰) プロダクトを直す いいえ
仕様変更が意図的だった 計画を更新してからテストを直す 承認後のみ
テストの書き方が悪い テストを直す(証明したい内容は変えない) はい
CI環境やネットワークの一時障害 環境を直す いいえ

「テストの書き方が悪い」場合だけHealerの出番、という整理です。プロダクトのバグで落ちているのに、テスト側だけ直して通してしまったら本末転倒ですよね。ここの判断は結局、人間が最初にざっくり見ておく必要があると感じました。

何でも直していいわけではない

Healerが提案してよい修復と、人間のレビューを必須にすべき修復は分けて考える必要があります。公式の考え方を整理すると、こうなります。

  • 直していいもの: ロケータの更新、待機時間の調整、前提条件となるテストデータの復元
  • 人間のレビュー必須のもの: assertionの削除・弱体化、skipやfixmeの追加、期待する金額・権限・件数などテストが証明している内容そのものの変更

要は「テストが何を保証しているか」を変えるような修復は、Healerに任せきりにしてはいけないということです。ロケータが変わっただけなら直していいですが、「エラーが出るのでassertionを消しました」となっていたら本末転倒です。

Healerがスキップを提案してくる場合もあるそうですが、これは「直せた」ではなく「壊れていそうだと診断した」というシグナルとして扱うべきだと公式ドキュメントにも書かれています。スキップにはオーナー・理由・期限をセットで付けるルールにしておかないと、いつの間にかカバレッジが減っていた、ということになりかねません。

CIで自動実行する時は診断ジョブに閉じ込める

Healerをローカルで対話的に使う分にはまだ安心なのですが、CIで自動実行させるとなると話が変わってきます。公式でも「Healerを自動化から実行するなら、信頼された・手動承認が必要な別ジョブに隔離してください」と案内されています。

具体的には次のような制約を置くのが安全そうです。

  • 読み取り専用の診断ジョブとして実行する(リポジトリへの書き込み権限は持たせない)
  • 渡す情報は失敗時の最小限のアーティファクトにとどめる
  • 提案されたパッチは通常のプルリクエストとして人間がレビューする

トレースには本番データやトークンが含まれる可能性がある、という注意点も公式にありました。これは正直、Healer固有というより自動修復エージェント全般に言えることですが、便利さに気を取られて見落としがちな部分だと思います。

よくある疑問

Q. Healerに直させたパッチはそのままマージしていいのでしょうか。

ロケータの更新程度であれば、レビューを軽くして早めにマージしてよいと思います。ただし前述の通り、assertionやテストの意図に関わる変更が混ざっていないかだけは必ず自分の目で確認してからにしましょう。差分は1行でも、中身は見てから流す。ここはサボらない方がいいところです。

Q. 既存のテストが大量にあっても使えますか。

使えます。ただしinit-agentsが生成するファイル構造を前提にしているので、既存のテストをすべてtests/配下に移す必要はないものの、Healerに渡すテスト名やファイルパスは正しく特定できる状態にしておく必要があります。移行が面倒な場合は、まず一部のテストだけHealerの対象にする、という段階的な導入がよさそうです。

触ってみた素朴な感想

ロケータ変更のような単純な修復であれば、Healerに任せるのは十分ありだと感じました。トレースを自分で開いて、DOMを見比べて、該当箇所を探して直す、という一連の単純作業から解放されるのは素直にありがたいです。実際にラベル違いのケースで試した時は、思っていたよりあっさり直ってしまい、少し拍子抜けしたくらいでした。

ただ、あくまでHealerが直すのは「テストの書き方」の部分です。プロダクトの不具合でテストが落ちているのか、テストの書き方が悪くて落ちているのか、その切り分けは結局人間の判断が必要になります(この切り分けをAIにどこまで任せられるかは、私自身まだ手探りです)。

個人的には「ロケータや待機時間まわりの単純な修復はHealerに任せる」「assertionやテストの意図に関わる部分は自分で見る」という線引きさえ決めてしまえば、日々の保守はかなり楽になりそうだと感じています。

Playwrightのテストエージェントは2026年時点でもまだ新しい機能で、バージョンが上がるたびに挙動が変わる可能性があります。この記事の内容も、試す時期によっては仕様が変わっているかもしれない、ということは正直に書いておきます。

まとめ

PlannerでテストのMarkdown計画を作り、Generatorでコードにして、Healerで保守する。Playwrightのテストエージェントはこの3段構えで、保守のつらさを減らす方向に振り切った機能だと感じました。

特にHealerは「ロケータが変わって落ちる」という一番地味で一番件数の多いつらさに直接効きそうです。ただし、assertionの中身やテストが証明している内容まで変えるような修復は人間のレビューを必須にする、という線引きだけは持ち込んでおくのがよさそうです。

実際に触ってみて気づいたことがあれば、コメントで教えてもらえるとうれしいです。

コメント

タイトルとURLをコピーしました