NTTドコモR&Dの技術ブログです。

試すのは簡単、仕上げるのは難しい。AIエージェント×Playwright MCPを使ったE2Eテストアプリの開発と苦労

自己紹介

NTTドコモ データプラットフォーム部(以下DP部)矢野です。

我々が提供している社内データ活用プラットフォームPochiでは、特定の部署に閉じない様々な部門のメンバが日々アプリを開発しています。

このPochiでのアプリ開発者を支援する取り組みとして、Playwright MCPを使った自動E2Eテストアプリを開発しました。

本記事では、その開発の経緯と工夫点について、支援メンバであるNTTデータ デジタルサクセスソリューション事業部 鈴木さん・臼倉さんに執筆いただきます。

社内データ活用プラットフォームPochi*1とは

私たちDP部は社内のデータ民主化を目指し、StreamlitとGoogle Cloudで圧倒的に使いやすいデータ活用プラットフォームを開発・推進しています。このプラットフォームは、24年度は30万時間もの業務効率化を実現し、直近では5,000人以上の社員に利用が拡大しています。

ASCII.jp:NTTドコモ、Streamlit利用の“ポチポチ分析アプリ”開発で社内データ活用を促進 (1/3)

想定読者

  • Playwright MCPをアプリケーションに組み込みたい方
  • Playwright MCPを使ってE2Eテストを自動化したい方
  • AIエージェント×Streamlitアプリケーションに興味がある方

モチベーション

Pochiプラットフォームの提供を開始して、数年が経過しました。

数年たった今も、日々新たなアプリが誕生したり、既存アプリの活発なアップデートが入ったりと、多くの開発者が活動しています。

その一方で、段々と以下のような課題も顕在化してきました。

  • 1人の開発者が担当するアプリ数が増加し、運用・保守で稼働がひっ迫する
  • 誰でも開発できるプラットフォームを目指している分、開発者のスキルレベルも様々であり、手動テストの比重が高くなる

その結果、たとえ実装が小さなアップデートでも反映には時間がかかり、アジリティを損なう原因となっていました。

上記課題の解決に向けては、自動テストの整備が不可欠です。

一方、自動テストの導入には一定の工数や学習コストが必要なため、なかなか普及しきっていないのも事実でした。

そこで、まずは自動テストの価値を多くの方に体験いただきたいと思い、Playwright MCPを使った自動E2Eテストアプリを開発することにしました。

プログラミング経験が少ない開発者でも簡単に実行できるため、 このアプリが自動テスト普及の呼び水となることを期待しました。

※また、LLMが呼び出せるブラウザ操作系ツールを試し、技術ノウハウを貯めることも裏ミッションでした。

全体像

Pochiプラットフォームにおいて、Streamlitアプリは本番環境・検証環境にデプロイされる運用となっています。今回開発したアプリは検証環境に対するテストをするものです。

本アプリは、テスト対象アプリへの接続情報(URL・ページ名)を受け取ると、Playwright MCPを介してエージェントが検証環境のStreamlitアプリを操作し、テストを実行します。

Playwright MCPとは?

Playwright MCPは、AIに自然言語で指示を出すだけで、ブラウザ操作を自動実行できるようにする仕組みです。Microsoftが開発したブラウザ自動化ツール「Playwright」を、MCP(Model Context Protocol)を通じてAIから利用できるようにしたものです。

Playwrightは、WebアプリケーションのE2E(End-to-End)テストを自動化するためのフレームワークです。例えば、「ボタンをクリックする」「フォームにテキストを入力する」といった操作をコードで記述し、ブラウザ上で自動的に実行させることができます。

このPlaywrightでは、人間がテストコードを記述する必要がありました。しかし、Playwright MCPを使うことで、AIエージェントに対して「ボタンAをクリックして」といった指示を出すだけで、AIエージェントがその内容を解釈し、実際のブラウザ操作を自動実行することが可能になりました。

どんなアプリを開発したか?

ユーザー導線

先述のように、多くの方に自動E2Eテストを体験いただくことが、本アプリの目的の1つです。

実行シナリオを自然文で全て入力するのも、「とりあえずやってみよう」を妨げる1つの要因になります。

そのため、今回のアプリでは、

  1. E2Eテストシナリオの自動生成
  2. シナリオに沿ったE2Eテストの実行

の2つを機能として具備することで、シナリオの生成からエージェントに任せられるようにしました。

シナリオ生成の際も、ユーザー入力は「テスト対象アプリのURL」と「テスト対象ページ名」に絞り、使い始めのハードルをできるだけ下げるようにしました。

エージェントアーキテクチャ

今回のアプリでは、2種類のLLMモデルを活用しています。

  • Claude Sonnet 4: AIエージェント
  • Gemini 2.5 Flash: コンテキスト要約、シナリオの構造化出力

タスク推進はClaudeに任せつつ、要約やシナリオ出力といった大きめのトークンを扱うパートは、コンテキストウィンドウに余裕のあるGeminiを使うこととしました。

エージェントは、タスク完了までループする、シンプルな構成です。 予期せぬ無限ループを防ぐため、一定の繰り返し回数に到達したらエラーを返す構成にしています。

工夫点

実装を進めていくと、意外と難しいポイントが多く、複数の課題に直面しました。 その中でも代表的だった課題と、それに対する工夫点についてご紹介します。

  • 課題1. コンテキストウィンドウのオーバーフロー・コスト増大
  • 課題2. シナリオフォーマットのぶれ
  • 課題3. Streamlitの特定コンポーネントの操作が困難

課題1. コンテキストウィンドウのオーバーフロー・コスト増大

Playwright MCPは、通常、アクセス先アプリのページ情報をアクセシビリティツリーとして取得します。そのため、エージェントループが回るたび(=ツールを呼び出すたび)に、一定量のトークンを消費します。

一方、テスト対象のアプリによっては、一通り操作しないと全体像が見えないものも存在します。操作の度にツール呼び出しが起こるため、おのずとイテレーションも増え、トークンがすぐに増大していきます。 これにより「コンテキストウィンドウに到達して操作が継続できなくなったり、1回の実行に500円~1,000円のコストがかかる」という事象が発生しました。

コンテキスト管理

まずはタスクを最後まで完遂させるため、コンテキストを管理し、必要に応じて要約処理を挟むこととしました。 一定の閾値に到達したら、エージェントとは別でLLMを呼び出し、これまでの経緯を要約してトークンを圧縮。改めて、アプリ操作のエージェントに渡しなおして操作を継続させることで、コンテキスト超過を防ぎながら最後までタスクを完遂できるようになりました。

プロンプトキャッシング

最も効果的だったのが、プロンプトキャッシングの有効化です。 今回はAnthropic SDKを用いてClaude Sonnet 4を呼び出していたため、リクエスト時にcache_controlパラメータを指定することで、有効化できます。

※ tools、system prompt、messageのそれぞれで独立して設定可能です。

# サンプルコード

# 1. ツール部分のキャッシュを有効化
tools = [
    {"name": "tool1", "description": "...", "input_schema": {...}},
    {"name": "tool2", "description": "...", "input_schema": {...}},
]
if tools:
    tools[-1]["cache_control"] = {"type": "ephemeral"}

# 2. システムプロンプトのキャッシュを有効化
system_prompt = [
    {
        "type": "text",
        "text": "あなたはBDDに基づいたテストシナリオを作成するQAエンジニアです...",
        "cache_control": {"type": "ephemeral"}
    }
]

# 3. メッセージのキャッシュを有効化
messages = [
    {"role": "user", "content": [...]},
    {"role": "assistant", "content": [...]},
    # ... 会話履歴が継続
]
if messages:
    messages[-1]["content"][-1]["cache_control"] = {"type": "ephemeral"}

# 4. API呼び出し
response = await anthropic.messages.create(
    model="claude-sonnet-4@20250514",
    max_tokens=4000,
    system=system_prompt,
    messages=messages,
    tools=tools,
)

Playwright MCPを使う場合、ページ内の情報を読み取りなおすイテレーションが高頻度で発生します。

類似した情報が繰り返し入力されるため、プロンプトキャッシングとの相性がよく、以下2つの効果が得られました。

  1. 1シナリオを実行するコストが約1/5に減少
  2. テスト完遂までにかかる時間も約2/3に短縮

課題2. シナリオフォーマットのぶれ

エージェントは与えられたシナリオに沿ってテストを実行するため、シナリオのフォーマットによっては期待通りに操作を実行できないケースがありました。 また、シナリオのフォーマットが一定でないと、出力のレビューやチューニングも困難になるという課題もありました。

シナリオの構造化出力

そこで、エージェントが出力したシナリオを、予め決めたフォーマットで構造化出力することとしました。

誰しもが読みやすく、かつ構造的に記述できるシナリオフォーマットが良いだろうと考えた結果、BDD(Behavior Driven Development)でよく使われるGherkin記法をベースとしました。 管理がしやすいように、シナリオIDや正常系/異常系の分類を追加したり、日本語表現にするなど、少しデフォルメしたものを使用しました。

上記のフォーマットをPydanticでスキーマ定義し、Gemini 2.5 Flashに構造化出力させることで、安定的にシナリオを出力できるようになりました。

from pydantic import BaseModel, Field
from typing import List

class GherkinTestScenario(BaseModel):
    """Gherkin記法をベースとしたテストシナリオモデル"""
    scenario_id: str = Field(description="シナリオID(例:1-1、1-2)")
    feature: str = Field(description="確認観点(何を確認したいか)")
    classification: ScenarioType = Field(description="分類(正常系、異常系)")
    scenario_description: str = Field(description="シナリオの説明")
    preconditions: List[str] = Field(description="前提条件のリスト") # Givenに相当
    operation_steps: List[str] = Field(description="操作手順のリスト") # Whenに相当
    expected_results: List[str] = Field(description="期待結果のリスト") # Thenに相当

課題3: Streamlitの特定コンポーネントの操作が困難

Streamlitアプリのテスト自動化を進める中で、「データフレーム内のチェックボックスがクリックできない」という課題に直面しました。

Playwright MCPのクリック操作

Playwright MCPには、以下の2つのクリック方法があります。

  1. アクセシビリティツリーベースのクリック - アクセシビリティツリー(Webページの構造情報)をもとにクリックする
  2. 座標ベースのクリック - 画面上の絶対座標(X,Y座標)を指定してクリックする

通常、1.のアクセシビリティツリーベースのクリックの方が高速かつ正確なため、Playwright MCPではこちらが標準的に使用されます。しかし、Streamlitのデータフレーム内のチェックボックスは、この方法ではクリック可能な領域として正しく認識されず、操作できませんでした。

一方で、2.の座標ベースのクリックでは操作が可能でした。そこで、データフレーム内のチェックボックスをクリックする際には、座標ベースの方法を採用することにしました。

正確なチェックボックスの座標位置の特定

座標ベースのクリックを用いることで、クリック操作自体は可能になりました。しかし、クリックすべき座標位置を正確に求めることができず、誤った場所をクリックしてしまう問題が起きました。

この課題に対処するため、画像認識とAIを組み合わせた、座標特定の専用ツールを作成しました。この座標位置特定ツールでは、以下の処理を行います。

  1. パターンマッチングでチェックボックス検出
    • 画面内のすべてのチェックボックスの座標を算出し、検出したチェックボックスにラベル(A, B, C...)を付与
  2. LLMで対象チェックボックスを判定
    • ラベルを付与したスクリーンショットをLLMに渡し、検出した全チェックボックスの中から対象となるチェックボックスのラベルを判定
  3. ラベルから座標に変換
    • 特定されたチェックボックスについてラベルから座標へ変換

解決策の全体像

データフレーム内のチェックボックスを操作する際は、以下の3ステップを踏むようAIエージェントに指示を加えました。

  1. スクリーンショットを取得
  2. 座標特定ツールでXY座標を算出
  3. 座標ベースでクリック実行

この仕組みにより、Streamlitのデータフレーム内のチェックボックスの正確な位置を特定し、クリック操作ができるようになりました。

今後の課題

この座標特定ツールは、スクリーンショットから画像認識で座標を算出する仕組みのため、スクリーンショット内に対象のチェックボックスが映っていない場合は検出できないという制約があります。

特に、データフレームの行数が多い場合、スクロール操作が失敗して対象のチェックボックスが画面外に残ってしまうケースがありました。

暫定的には、スクリーンショットの取得範囲を広めに設定することで対処していますが、より確実なスクロール制御の実装が今後の課題となっています。

アプリ開発者からのフィードバック

各所で苦労した本アプリでしたが、工夫を重ね、何とかリリースまでたどり着くことができました。 多数の開発者がアプリに触れてくださり、日々の開発業務で活用いただいています。

実際に活用する中でのフィードバックも複数寄せられており、中でも特徴的なものをご紹介したいと思います。

UIの盲点を発見

自動生成されたシナリオを確認すると、開発者自身が想定していなかった期待結果が含まれていることがありました。AIエージェントがアプリ画面を解釈してシナリオを生成するため、「ユーザーは画面を見てこうした挙動を期待するのか」という気づきが得られたそうです。 これは、コードベースのテストでは得られにくい気づきであり、Playwright MCPによるテストならではの視点だと考えています。

テストの属人性軽減

開発担当者の離任・引継ぎによりテストの属人性が課題となっているチームから、感謝の声をいただけました。 特に、これまで自担当でなかったアプリを引き継いだ際に、シナリオのメンテナンスに苦慮されていたそうですが、本アプリを活用することで、一定の負荷軽減につながったとのことです。

アクセスするアプリ環境

今回は、検証環境にデプロイされたアプリにエージェントがアクセスして、テストを実行する形をとりました。しかし「検証環境デプロイ前に、ローカル環境での動作確認に使いたい」という声も、一部の開発者から寄せられました。 その方々には、このアプリをフォークしてローカル環境で使えるようにしたものを活用いただいており、こちらも複数の開発者に利用されています。

あとがき

本記事では、Playwright MCPを使った自動E2Eテストアプリの開発と苦労ポイントについてご紹介しました。 アプリとして仕上げるには難しいポイントが多々ありましたが、その分多くの学びを得ることができました。

今後も、MCPサーバーをはじめとした最新技術を積極的に取り込みつつ、アプリ利用者・開発者双方への価値提供に取り組んでいきたいと考えています。

*1:Pochiは社内の開発コードネームです