1. はじめに:なぜ私たちはAIによるE2Eテスト自律生成を目指したのか
はじめまして。食べログオーダーチームのしら酢です。本記事では、食べログオーダーの店舗管理画面(以下、店舗管理画面)で行なったE2Eテストの自律生成の取り組みについて紹介します。
店舗管理画面は、メニュー・テーブル管理、UI設定、ダッシュボードなど飲食店業務の中核を担う多機能なWebアプリケーションです。複数のエンティティに跨る仕様の複雑さに加えて外部POSとの連携といった特殊条件が絡み合うことで検証環境の整備が難しく、リグレッションリスクも高いため、テストの充実は長年の課題でした。
2023年からの大規模リプレースを経て、単体テストの文化は根付きました。カバレッジは22.48%から残り僅かなリプレース未完了のページを除けば約85%に達しています。
一方でE2Eテストは別の歩みをたどりました。2024年の4月から5人のメンバーが隙間時間を使って初期テストを整備しましたが、機能実装と並行してE2Eを作り続ける文化までは根付きませんでした。 単体テストとStorybookの拡充まではできたものの、機能のタイムトゥマーケットを最優先した結果、E2Eテストに割く工数の費用対効果を説明しにくかったこともあり、本取り組みまでE2Eテストが追加されるのは障害発生後の再発防止としてのみにとどまりました。テストケースの推移を見ると、2024年10月の103件から2025年3月には168件と伸びてはいるものの、増加ペースはきわめて緩やかでした。
単体テストはコードと1対1対応で書けるのに対して、E2Eテストは画面仕様・APIモック・操作フローの把握など、書き始めるまでのコストが高い。それが参入障壁になっていたことも要因として考えられるでしょう。
「このコストを下げるためにAIを使えないか」というのが、今回の挑戦の出発点です。
2. 挑戦の概要:ゴールと基本戦略
ゴール:仕様調査からテスト成功まで自律化する
今回の挑戦で目指したゴールは、「仕様調査からE2Eテストの実装・成功まで、AIが自律的に完遂すること」です。
実装における「成功」の定義は明確に設定しました。
- ESLintのチェックが通ること
- TypeScriptのビルドが通ること
- Playwrightのテストが通ること
これら3つを満たして初めて1シナリオの自律生成が完了とみなします。 一方でテストシナリオの品質は後述する静的解析ツールの作成・改善という形で人間が責務を負い、AI任せにしないことで一定品質の維持を狙っています。
制約:コストと安定性を両立する設計
設計の基本方針は「新人に仕事を渡すように、確実にこなせる単位まで分割し、必要な情報を整える」というものです。AIも同様に、曖昧な指示や過大なタスクでは失敗しやすく、明確な役割と適切なコンテキストを与えることで安定した出力が得られるという仮説の検証がこの取り組みの副次的なテーマでした。
コスト面と再現性を考慮し、テスト実装はClaude Haikuでも実行できるよう設計しました。ただし、仕様調査やコード解析など高度な理解が必要な工程ではSonnetを使用します。また、1シナリオにつき1セッションというルールを設けることでコンテキストを小さく保ちます。
APIはすべてモック(MSW)を使用しました。管理画面という性質上、フロントエンドだけでも調査範囲は膨大であり、バックエンドまで含めるとAIに与えるべきコンテキストが更に増えてしまうためです。差し当たってのE2Eとしての本来の意義は薄れますが、範囲を狭めても実現可能性を測るためには必要な割り切りだったと考えています。そして、この取り組みの初期段階が終わった現在ではモックを外せるところから外しているところです。
成功の前提:アーキテクチャがAI自律生成を可能にした
この挑戦が成立した背景として外せない要素が、リプレース時のアーキテクチャ設計です。
店舗管理画面のリプレースでは、当初からコードジェネレータの作成までを視野に入れた設計を採用していました。AIという文脈のおかげでようやく言語化しやすくなった「高い予測可能性とエントロピーの抑制」、つまりはコードの一貫性と構造的整合性の向上を目的とし、ページ構造・ファイル配置・命名規則に一貫したルールが徹底されています。これはOpenAIが「ハーネスエンジニアリング」を紹介した記事の一節と同じ考え方です。
エージェントは厳格な境界と予測可能な構造を持つ環境で最も機能を発揮する
この設計思想が、LSPを使った静的解析ツールの自作を容易なものとし、AIによる自律的なコード生成・テスト生成を可能にする土台となりました。後述するように、この一貫性から外れた2ページでは自律生成は失敗しています。静的解析ツールも作って終わりでなく、実行中に情報の追加や整理などを繰り返した上でエージェントにはMCPツールとして提供していました。
ルールが徹底されたコードベースはAIが解釈すべきパターンの可能性を絞り込み、推論空間のエントロピーを下げます。静的解析ツールはその構造をコンテキストへ正確に供給するための補助手段であり、一貫性のないコードベースにツールだけを持ち込んでも同じ結果は得られません。アーキテクチャの一貫性はツールの有効性の前提条件であり、その逆ではありません。
3. 個別アプローチからのオーケストレーション:個々の設計が噛み合い、E2E生成へ
この挑戦が実現した背景には、複数の取り組みが独立して始まり、やがて噛み合っていった経緯があります。
2025年9月からマルチエージェント・CLI自動化・セッション管理を目的とした自作エージェントツールの開発を進めていました。しかし当時社内で使っていたCursorではCLIからエージェントを呼び出すことができないために、この段階ではコピー&ペーストによる半自動化が限界でした。
転機は社内へのClaude Code導入です。Claude Codeはそれ自体がCLIとして動作しワンショットモードでエージェントを起動できます。「AIをCLIとして呼び出す」という手段が確立されたことで、PythonスクリプトからAIエージェントを完全自動制御するパイプラインの構築が可能となりました。
個人レベルで育てていたエージェント設計の知見と、組織として導入されたClaude Codeが結びつき、E2Eテストの自律生成というアイデアが実装に踏み込めるようになったのです。
4. 【技術解説】AIマルチエージェントによるテスト生成の仕組み
エージェントの全体構成
起点はシンプルです。対象ページのパスを1つ渡すだけで、パイプライン全体が自律的に動き始めます。
python3 scripts/generate_e2e.py others/hall_staff
あとはPythonがPlaywrightのUIモードを起動し、Conductorを立ち上げ、TODOの完了を監視するだけです。プロンプトやロール定義はスクリプトやConductorが自動で投入してくれる形です。テンプレートに対象ページのパスなどの変数を差し込むだけの簡単な仕組みですが、それゆえアーキテクチャに一貫性がなければ実現が難しいという理由の一つでもあります。
PlaywrightのUIモードはテスト失敗時に画面の状態を確認できるよう、--ui-portを指定してURLを実装エージェントに渡しています。
テスト生成パイプラインは、Pythonスクリプトを起点とするマルチエージェント構成です。
Python(オーケストレーター)
└── Conductor(サブエージェント管理)
├── Manager(仕様調査・シナリオ作成)
├── Implementor(テスト実装)× シナリオ数
└── Verifier(テスト検証)× シナリオ数
PythonはNext.jsのページパスを受け取り、Conductorを起動してからはTODOリストのchecked状態を監視するだけです。実際の作業判断はすべてConductorに委ねます。
処理の大まかな流れは以下のとおりです。
- PythonがPlaywrightのUIモードをバックグラウンドで起動し、Conductorセッションを作成
- ConductorがManagerを起動。ManagerはドキュメントとLSPによる実装コード解析を行い、テストシナリオとTODOリストを提案
- PythonがConductorに「このTODOを処理せよ」と1件ずつ指示を送る
- ConductorがImplementorを起動 → Implementorがテスト実装 → 成功時の知見を
tips.mdに記録 - ConductorがVerifierを起動 → VerifierがE2Eテストを実行確認
- 成功時: 変更をコミット
- 失敗時: 最大3回まではConductorからリトライ
- ConductorがTODOをchecked=Trueに更新、全TODO完了後にPythonが終了

各エージェントの役割をまとめると以下のとおりです。
| エージェント | 役割 |
|---|---|
| Conductor | サブエージェントの管理・TODOリストによる進捗管理・リトライ判断 |
| Manager | ドキュメント・実装コードのレビュー、テストシナリオ提案 |
| Implementor | テストコードの実装、成功体験のtips.mdへの記録 |
| Verifier | テスト実行・検証・成功時のコミット |
コンテキストウインドウを「設計する」という発想
今回の設計で意識したのがコンテキストウインドウの密度です。AIは会話の履歴や参照情報を「作業机の上」のように使って推論します。机の上が散らかるほどAIの推論も乱れる——膨大な履歴を積み上げるよりも、今必要な情報だけを厳選して渡す方が安定した出力が得られます。
「1シナリオ1セッション(Haiku)」という設計はこの考えに基づいています。シナリオをまたいで同じセッションを使い回すと、過去の試行錯誤が蓄積してコンテキストが肥大化します。新しいシナリオでは新しいセッションを使うことで、毎回クリーンな状態から推論させられます。
一方で「過去の学びを捨てる」ことへの対処が必要になります。それがツールとTipsによる知識の外部化です。
LSPを活用したプロジェクト固有MCPツール
依存関係の調査や複数CLIをまとめて実行することによりトークン効率の向上を狙うため、一例ですがエージェントには以下の様なプロジェクトに特化したMCPツール群を自作して提供しました。
- ts_extract_component_tree:Next.jsのpageファイルを起点に条件分岐を含むコンポーネントツリーを提供
- ts_analyze_dependencies:LSPを使った依存関係ツリーの提供
- ts_checker:Lint + Buildの実行
- ts_query_api:APIエンドポイントの一覧取得
これらのツールにより、エージェントはコードを手動で読み歩くことなく、ページの構造・依存関係・APIを正確に把握できます。静的解析が容易なアーキテクチャ設計と、それを活かすMCPツールの組み合わせが、エージェントの調査精度を底上げしました。
セッション間の知識共有:Tipsからの学習
「1シナリオ1セッション」はコンテキスト管理には有効ですが、そのままでは各セッションが孤立してしまい同じ失敗を何度も繰り返すことがありました。これを補うために知識の外部化という仕組みを設けました。
Implementorは、テスト実装で失敗から成功に転じた際の解決策を tips.md に書き残します。この知見は次以降の実装でも活かせる資産です。次のImplementorはセッション開始時にこのファイルを参照することで、同じ問題で詰まることを避けられます。
さらに自動化パイプラインの外で、Curatorエージェントが定期的にこれらのtips.mdを収集し、プロジェクト全体で共有されるFAQ形式の知識ベース(e2e/tips/)に体系化します。現在ではMSWモック設定・testid命名規則・アニメーション待機など30件以上のTIPSが蓄積されています。
e2e/tips/ ├── index.md ← FAQ マスターインデックス ├── testidサフィックス自動追加.md ├── MSWモック設定順序.md ├── アニメーション待機.md └── ...(30件以上)
Surveyorによる事前測量
後から追加した仕組みとして、Surveyorエージェントによる事前測量があります。Implementorが実装に着手する前にSurveyorがページを調査し、全testid・操作方法(click/check/fill)・条件表示パターン・API呼び出しをblueprint.mdとして記録します。
Implementorはこのblueprintを参照することで「どの要素をどう操作すればよいか」を迷わず把握でき、Playwright MCPなどを使い手探りする時間を削減できます。SurveyorとCuratorはいずれも自動化パイプラインの外に位置しますが、パイプライン全体の品質を底上げする役割を担っています。
5. 実践から得られた3つの学びと「AIのクセ」
18ページに対して自律生成を実施した結果、いくつかの明確なパターンが見えてきました。
学び① アーキテクチャの一貫性の重要性
ルールから外れたページはAIも扱えなかった。
今回対象としたE2Eが書かれていなかった18ページ中2ページで自律生成が最終的に失敗しました。この2ページに共通していたのは、「プロジェクトのアーキテクチャルールから逸脱したコードが混在していた」という点でした。禁止されているはずのコードパターンが残っていたり、他のページと異なる構造が採用されていたりしていました。
ツールにせよAIにせよ「ルールが一貫している」という前提でコードを解釈し、テストを生成します。例外がある場合、その例外を正しく扱えません。
逆に言えば、AIが扱えないコードはエンジニアも扱いにくいコードです。自律生成の失敗は、コードベースの改善を促すシグナルとしても活用できます。
学び② 要素特定の難しさとFlakyテスト
nth-childへの依存はテストを不安定にした。
表示・非表示が頻繁に切り替わる画面では、位置ベースの要素特定(nth-child等)が不安定になります。AIは試行錯誤の中でnth-childに頼った実装を選びやすく、結果として表示状態によって別の要素をクリックしてしまうFlakyなテストが生まれました。
これへの対策として、Surveyorが事前測量でtestidを網羅的に記録し、Implementorがtestidベースの要素選択を徹底する方針を確立しました。またCuratorが「testidなし要素の複数フィルタ探索」などの対策パターンをFAQとして体系化し、後続のImplementorが同じ落とし穴にはまらないようにしました。
学び③ AIの「疲弊」との付き合い方
テストファイルが1500行を超えると失敗しがちになる。
同一のテストファイルにシナリオを追加し続けると、ファイルが肥大化します。テストファイルが1500行を超えてくると実装の品質が落ち始め、失敗率が上がりました。コンテキストウインドウの圧迫、あるいはClaude CodeのRead/Replaceツールが大きなファイルを扱う際の問題が原因として考えられます。
対策として、ファイルが1500行に近づいたら「疲弊する前に」テストファイルを分割するルールを設けました。1ファイルに詰め込むのではなく、機能単位や画面領域でファイルを分けることで、安定した品質を保てるようになりました。
6. 成果と考察:そして、テストケースは2倍超になった
定量的成果
| 日付 | テストケース数 | 備考 |
|---|---|---|
| 2024-10-07 | 103 | 初期実装 |
| 2025-03-03 | 168 | 再発防止のテストケースの積み上げ |
| 2026-01-07 | 368 | 自律生成したE2Eテストのマージ |
18ページ中16ページで自律生成に成功し、実働20日ほどでテストケースが168件から368件へと2倍超に増加しました。E2Eテストの自律生成はまだ事例の少ない取り組みですが、食べログオーダーの管理画面というスケールでは十分スムーズに実現できたと言えます。 さらにこの期間中、テスト増加に伴うCI実行時間の削減を目的としたテスト並列化や、以前から問題だったFlakyテストの安定化も並行して実施しており、E2Eテスト基盤全体の底上げを同時に達成しました。
短期的な成果としてはリリース後2週間でリファクタリングの影響範囲の調査漏れによるランタイムエラーを検知しました。Cannot read properties of undefined という実行時エラーで、リファクタリングで変更したプロパティが別コンポーネントで参照されていたことが原因です。早くも本番障害になる前にE2Eテストが検知できた実例となりました。
定性的成果
実装担当者への簡単なヒアリングでは、いずれも「必要なテストケースは網羅されていると思う」という評価を得られました。
特に印象的だったのは、人間だとスキップしがちな細かいケースが網羅されているという点です。
- 入力フォームからのバリデーションエラーの確認
- バルーンのメッセージ内容の確認
- 想定外のエラーレスポンス時の挙動
これらは手動でテストを書く際には「まあいいか」と省略されやすいケースですが、AIは仕様を読み込んで網羅的にシナリオを列挙するため、こうした細部まで拾います。
テストケースの過剰な細分化を防ぐ工夫
一方で、AIには「前提条件だけを独立したテストケースとして挙げる」というクセがあります。たとえば以下のように、完全なフローに至るまでの途中段階をそれぞれテストケースとして提案してくることがあります。
❌ 悪い例:中間状態を独立したテストケースにする テストケース1: 代理注文設定トグルをクリック テストケース2: 代理注文設定トグルをクリック → 伝票サンプルボタン確認 テストケース3: 代理注文設定トグルをクリック → 伝票サンプルボタンをクリック → モーダルを閉じる ✅ 良い例:完全なユーザーフローのみをテストケースにする テストケース: 代理注文設定トグルをクリック → 伝票サンプルボタンをクリック → モーダルを閉じる
テストケースが過剰に細分化されると、実行時間が伸びる・メンテナンスコストが上がるという問題が生じます。「完全なユーザーフローとして成立するシナリオのみをテストケースとする」というルールをManagerへの指示に組み込み、過剰な細分化を防ぐようにしました。
7. おわりに:複雑なプロジェクトこそAIと共に
今回の挑戦を振り返ると、自律生成を成功させるために必要だった条件がはっきり見えてきます。
アーキテクチャの一貫性:ルールが明確で予測可能な構造は、AIが最も力を発揮できる環境です。コードジェネレータを想定した設計が、そのままAI自律生成の基盤になりました。
適切なエージェント役割設計:「調査」「実装」「検証」を別エージェントに担わせ、それぞれのコンテキストを小さく保つ設計が安定した動作を生みました。
セッション間の知識継承:Tipsファイルの蓄積とCuratorによる体系化により、過去の失敗が次のエージェントの成功につながる仕組みを作りました。
AIのクセへの対処:過剰な細分化傾向・Flaky化しやすい要素特定・ファイル肥大化での劣化など、実際に動かして初めてわかったAI固有の課題を一つひとつルールとして対策しました。
設計ポリシーの一貫性:アーキテクチャによる予測可能性、エージェントの役割分離、コンテキストの密度設計——これらは独立した工夫として列挙できますが、その根底には「低エントロピーに収束させる」という共通の設計ポリシーがあります。各要素が同じポリシーで貫かれていたからこそ噛み合い、自律生成という結果に繋がりました。個別の条件を取り入れるよりも、このポリシーを軸に設計全体を統一することが本質的な再現条件かもしれません。
今回の試みを通じて、AIに実装を任せるための土壌をより良いものにしておくことは当然ながら、以前から持っていた以下の仮説への手応えを得ることができました。
- コンテキストウインドウに乗せるものは小さく高密度であるべき
- OJT資料のように、つまずきやすい部分を改善し続ける
- 確実に完遂できる単位までタスク分割を行うことが重要である
とはいえ、品質チェックゲートを通せばいいだけの箇所に検証エージェントを配置したのは富豪的という側面もあり、ベンダー依存の削減やコードベースへの親和性を考えると独自の推論基盤も視野に入ってくるなど、今後の保守運用も含めた課題は残り続けます。執筆時点で2025年11月の実装から3ヶ月以上が経過しており、更に解像度が上がったことを生かして日々改善を進めています。
E2Eテストの作成コストに悩んでいるチームに、一つの選択肢として届けば幸いです。
最後に食べログでは一緒に働く仲間を募集しています!複雑な仕様やリグレッションリスクに向き合いながら、AIで開発の課題を解決したいエンジニアの方からのご応募をお待ちしています。