
この記事は、「NEWT Product Advent Calendar 2025」Day17 および「ソフトウェアテスト Advent Calendar 2025」Day23 の記事となります。
「NEWT Product Advent Calendar 2025」17日目は、令和トラベル QAエンジニアのOsuが、Androiアプリエンジニア ともながからバトンをもらい執筆。ぜひ、最後までごらんください!
1. はじめに
E2Eテストの自動化は、多くのQA組織にとって「やりたいけど、維持できない」課題の代表格の一つです。
UIが変わればセレクターが壊れ、フローが変われば手順が壊れる。
気づけばテストコードの修正に追われ、本来の品質保証活動に時間を割けなくなるようなこの悪循環を経験したことがある方も少なくないと思います。
しかし、2025年にこの状況を変える技術がいよいよ実用化されました。それが Playwright Test Agents と Claude Code Skills の組み合わせです。
TL;DR
本記事の結論💡
- E2Eテスト実装にPlaywright Test Agents単体では不十分 - プロジェクト固有のルールを注入する必要がある
- Claude Code Skillsでルールを注入する - コーディング規約、Page Object、認証パターン等をSkillに定義
- プロンプト設計が品質を決める - 優先順位の明示と具体例の提示がポイント
- チーム全体で再利用可能な仕組みを作る - Gitで管理し、継続的に改善
対象読者
- E2Eテストのメンテナンスコストに課題を感じているQA/テストエンジニア
- AIを活用したテスト自動化に興味がある開発者
- チームでE2Eテストの品質を標準化したいリーダー
本記事では、「AIがなんとなくテストを書いてくれる」レベルを超え、プロダクション品質のテストコードを安定して出すための具体的な設計を共有します。
2. 課題:なぜE2Eテストは維持できないのか
E2Eテストの最大の課題はメンテナンスコストです。
テストを書くこと自体は難しくありませんが、UIの変更に追従し続けるコストが高くなりがちです。
E2Eテストが維持困難になるよくある要因
1. UIの変更に対する脆弱性
- セレクターの変更:ボタンのクラス名やIDが変わるとテストが壊れる
- レイアウトの変更:要素の位置や階層構造が変わるとテストが壊れる
- テキストの変更:ボタンのラベルやエラーメッセージが変わるとテストが壊れる
2. テストの不安定性(Flaky Tests)
- 非同期処理の待機が不適切で、タイミングによって成功・失敗が変わる
- 外部サービスへの依存により、ネットワーク状況で結果が変わる
- テストデータの状態に依存し、実行順序で結果が変わる
3. 実行時間とフィードバックの遅さ
- ブラウザの起動・操作に時間がかかり、CI/CDパイプラインが遅くなる
- 失敗時のデバッグに時間がかかり、修正コストが増大する
4. テストコードの重複と肥大化
- 同じ操作(ログイン、ナビゲーション等)が複数のテストに散在する
- Page Objectパターンなどテストデザインが徹底されず、変更時の影響範囲が広がる
これらの課題に対し、AIを活用したアプローチが注目されています。
既存アプローチとその特徴

現在、AI×E2Eテストには「AIがどこまでテスト生成・実行に関与するか」という観点で、主に3つのレベルがあります。
L1: コード生成支援(GitHub Copilot等)
特徴:
- コード生成支援ツールとして優秀な能力を持つ。
copilot-instructions.md等を活用することで、プロジェクト固有のルールを適用させることが可能。
課題:
- ブラウザを直接操作できないため、実際の画面を見ながらのテスト生成ができない。
- 生成されたコードが実際に動くかどうかは、実行してみるまで分からない(ハルシネーションのリスク)。
L2: 実行可能なテスト生成(Playwright Test Agents)
特徴:
- 2025年10月(v1.56)より公式導入された機能。
- MCPを通じてAIがブラウザを直接操作可能。
- 「画面を見て、操作して、テストコードを生成する」という理想的なワークフローを実現。
課題:
- 汎用性が高い反面、プロジェクト固有のルール(コーディング規約や独自ライブラリの使用など)が適用されにくい。
- 生成されたコードが、既存のコードベースと一貫しない(バラつきが出る)問題がある。
L3: 実行・修復まで含む自律型テスト(Testim、mabl、Katalon等)
特徴:
- 自己修復(Self-Healing)機能が強力。
- テストの生成から実行、失敗時の自動修復まで一貫してAIが担当。
課題:
- ベンダーロックインのリスクがあり、カスタマイズの自由度が制限される。
レベル別比較表
レベル | ブラウザ操作 | プロジェクトルール適用 | カスタマイズ性 |
L1: コード生成支援 | × | ○ | ○ |
L2: 実行可能なテスト生成 | ○ | × | ○ |
L3: 自律型テスト | ○ | △(ツール仕様に依存) | × |
私たちが目指したのは、「実際に画面を操作しながら」、かつ「NEWT固有の規約に従った」コード生成です。
3. 解決策の全体像:Playwright Test Agents × Claude Code Skills

この課題に対し、2024年11月のAnthropicによる MCP (Model Context Protocol) の発表、そして2025年10月のPlaywright v1.56による Test Agents の公式導入が、大きな転換点となりました。
なぜClaude Codeなのか
Playwright Test Agentsは、MCPに対応したAIツールであれば利用できます。その中でClaude Codeを選んだ理由は、エージェント的な自律動作にあります。
1. マルチステップワークフローの自律実行
Claude Codeは「既存Page Objectを検索 → ブラウザ操作 → コード生成 → 型チェック」という一連のワークフローを自律的に実行できます。
Copilotのようなコード補完ツールは、ユーザーがエディタでコードを書いている最中に補完を提案する「ユーザー主導」のワークフローです。一方、Claude Codeはタスクを与えると、自分でファイルを探索し、必要なコマンドを実行し、結果を確認しながら作業を進める「AI主導」のワークフローが可能です。
2. 動的なコンテキスト収集
Claude Codeはコードベースを能動的に検索し、必要なファイルを読み込んでコンテキストを構築します。「このPage Objectを使えばいい」「この定数ファイルをimportすべき」といった判断を、既存コードを実際に読んで行います。
なぜSkillsなのか
Claude Codeだけでも十分強力ですが、Skillsを使うことでさらに効果的になります。
開発の経緯:まずサブエージェントで試した
私たちは最初、
.claude/agents/ディレクトリにサブエージェントとしてGenerator Agentを実装しました。.claude/ └── agents/ └── playwright-test-generator.md # サブエージェント版
サブエージェントは、Claude CodeのTask toolから呼び出す形式です。複雑なタスクを自律的に実行できるため、E2Eテスト生成のような多段階処理には適しています。
しかし、チームで運用するには課題がありました。
- 呼び出しが煩雑
- Task toolで明示的に呼び出す必要がある
- 新規メンバーは「このエージェントを使う」という知識が必要
- コンテキストの引き継ぎが限定的
- サブエージェントは独立したコンテキストで動作
- メインの会話コンテキストとの連携が弱い
- 機能の存在に気づきにくい(Discoverabilityが低い)
- 「E2Eテストを書きたい」と言っても自動で使われない
- 明示的な指示が必要
Skillsの優位性
Claude Code Skillsは、これらの課題を解決します。
.claude/ └── skills/ └── e2e-implement-skill/ └── SKILL.md # Skills版
1. 自動的に呼び出される
Skillsは
descriptionに基づいて、Claude Codeが自動的に判断して呼び出します。「E2Eテストを実装して」と言えば、適切なSkillが自動で選択されます。--- name: e2e-implement-skill description: NEWTプロジェクトのE2Eテストを実装するためのスキル。 Playwrightを使用したPage Object Modelパターンに基づくテスト実装をサポートします。 ---
2. メインコンテキストと統合
Skillsはメインの会話コンテキスト内で動作します。これにより、ユーザーとの対話を継続しながら、Skillの知識を活用できます。
3. チーム全体で一貫したルール適用
Skillsはプロジェクトにコミットされるため、チーム全員が同じルールでテストを生成できます。新規メンバーのオンボーディングも容易です。
4. バージョン管理と改善サイクル
.claude/skills/はGitで管理されるため、プロンプトの改善履歴を追跡できます。チームでレビューしながら、継続的にSkillを改善できます。移行のポイント
サブエージェントからSkillsへの移行で変更した点:
項目 | サブエージェント | Skills |
配置場所 | .claude/agents/ | .claude/skills/ |
呼び出し | Task tool経由 | 自動判断 or Skill invocation |
コンテキスト | 独立 | メイン会話と統合 |
descriptionの役割 | 説明 | 呼び出し条件の判断基準 |
特に、
descriptionの書き方が重要です。Claude Codeが「このSkillを使うべきか」を判断する材料になるため、いつ使うべきかを明確に記述します。SKILL.mdの構成例
--- name: e2e-implement-skill description: NEWTプロジェクトのE2Eテストを実装するためのスキル。 Playwrightを使用したPage Object Modelパターンに基づくテスト実装をサポートします。 --- # E2E Test Generator for NEWT ## 使用条件 - E2Eテストの新規作成が必要な場合(詳細割愛) ## コーディング標準 [docs/e2e.md と同等の内容] - Locator優先順位 - 待機処理ルール - Page Object Model強制 - 認証パターン - PC/SP分離ルール ## テスト生成フロー 1. 既存Page Objectを検索(packages/e2e/pages/) 2. MCPツールでブラウザ操作を実行 3. コード生成 4. 型チェック(pnpm -F @newt/e2e check-types) ## 実装例 [具体的なコード例]
このSkill定義によって、私たちの要求に対して、AIが適切にE2Eテストコードを生成してくれるようになりました。
4. 仕組みの中核:Generator AgentをSkillで制御する
Playwrightが提供する3つのAgent

2025年10月リリースのPlaywright v1.56以降、3つのTest Agentsが提供されています。
Agent | 役割 | 主な機能 |
Planner | テスト計画 | アプリを探索し、Markdown形式のテスト計画を生成 |
Generator | テスト生成 | セレクタを実際に検証しながら、Markdownの計画書をPlaywrightのテストコードに変換 |
Healer | テスト修復 | テストスイートを実行し、失敗したテストを自動修復 |
これらは「Planner → Generator → Healer」の流れで連携して動作します。
Plannerがテスト計画を作り、Generatorがコードに変換し、Healerが壊れたテストを修復する。
テスト自動化の理想的なサイクルをAIが支援するコンセプトです。
▶︎ セットアップは以下のコマンドで行います:
npx playwright init-agents --loop=claude
公式Agentをそのまま使った場合の課題
公式のAgentは汎用的に設計されているため、プロジェクト固有の要件には対応していません。
実際に使ってみると、以下の課題に直面しました。
- プロジェクト固有のコーディング標準が適用されない
- 生成されたコードが既存のコードベースと一貫しない
- セレクターの選び方がプロジェクトの方針と異なる
- Page Object Modelが強制されない
- 直接セレクターを使ったコードが生成される
- 既存のPage Objectを活用しない
- 環境固有の対応が考慮されない
- reCAPTCHA、3DS認証等の特殊なフローに対応できない
- 認証状態の前提条件が不明確
- PC/SPの分離が自動化されない
- レスポンシブ対応のテストが考慮されない
- ビューポート設定が手動で必要
NEWTでの解決アプローチ
私たちは、公式のGenerator Agentをベースに、NEWT固有のルールをプロンプトとして埋め込みました。さらに、これをClaude Code Skillsとして実装することで、チーム全体で再利用可能な形にしています。
Generator Skillのプロンプト構造
私たちが設計したGenerator Skillのプロンプトは、以下の構造を持ちます。
--- name: playwright-test-generator description: NEWT旅行予約プラットフォーム向けのPlaywrightテスト生成エージェント tools: [MCPツール一覧] model: sonnet --- # 役割定義 You are a Playwright Test Generator specialized for NEWT travel booking platform... # コーディング標準(NEWT固有) ## Directory Structure ## Priority for Element Selection ## Waiting and Synchronization Rules ## Page Object Model Requirements ## Test Structure Requirements # 認証システム ## Default Authentication Setup ## Test Fixtures # コード生成優先ルール ## Priority 1: Element Selectors & Robustness ## Priority 2: Flow & Navigation from MCP Behavior ## Priority 3: Values & Test Data # テスト生成ステップ 1. 既存Page Objectの検索(必須) 2. ブラウザ自動化の実行 3. テスト生成 4. 型チェック検証 # 実装例 [具体的なコード例]
5. NEWTのE2Eルールセット設計(AIに注入する規約)
本章では、Generator Skillに埋め込んだルールセットを解説します。これらのルールにより、AIは既存のコードベースと一貫したテストコードを生成できるようになります。
各セクションでは、一般的なベストプラクティスとNEWTでの具体的な実装例を分けて記載しています。自社プロジェクトに適用する際は、ベストプラクティスを参考にしつつ、実装例を自社の要件に合わせてカスタマイズしてください。
5.1 要素選択(Locator)の優先順位
ベストプラクティス
保守性と堅牢性を高めるため、以下の優先順位を徹底させています。これはPlaywright公式ドキュメントでも推奨されているベストプラクティスです。
// 優先度1: セマンティック(最優先) page.getByRole('button', { name: '検索する' }) page.getByLabel('出発地') page.getByPlaceholder('都市や空港名') // 優先度2: テキストベース page.getByText('東京発') // 優先度3: data-testid(最後の手段) page.getByTestId('search-button') // 避けるべき例 // page.locator("div[class^='Header_title']") // → クラス名はビルド時に変更される可能性があり、テストが不安定になる
なぜこの優先順位なのか?
- セマンティックロケーターは実装変更に強い
- 「検索するというラベルを持つボタン」を探します。HTMLの構造が変わっても、ボタンのラベルが同じなら動作し続けます。
- テストコードが「何を検証しているか」が明確になる
- data-testidは「最後の手段」として温存
- data-testidはテストのために明示的に付与した属性で、アクセシビリティ自体の検証にはならないため最終手段とする
NEWTでの実装例:data-testid命名規則
NEWTでは、data-testidを使う場合の命名規則を以下のように統一しています。
{コンポーネント名}-{variant1}-{variant2}
例:
tourDetail-header-title, searchBox-destination-buttonこの命名規則により、テストコードを読むだけで「どのコンポーネントの何の要素か」が分かります。
5.2 待機処理のルール
ベストプラクティス:Flaky testを防ぐ待機パターン
Flaky test(不安定なテスト)の主な原因は、不適切な待機処理です。プロンプトには以下のルールを埋め込んでいます。
## Waiting and Synchronization Rules: - **NEVER use arbitrary waits (sleep, wait)** - Use URL or title assertions for navigation verification - Use explicit conditional waits only when necessary
正しい待機パターン
// 良い例: 明示的な条件待機 await expect(page).toHaveURL(/\\/tour\\/detail\\//) await expect(page.getByRole('heading', { name: 'ツアー詳細' })).toBeVisible() // 必要な場合の条件付き待機 await page.waitForURL(/\\/tour\\/detail\\//) await page.waitForSelector('[data-testid="tourDetail-xxx"]')
expect().toHaveURL() や expect().toBeVisible() は、条件が満たされるまで自動的にリトライします。明示的なsleepは不要です。NEWTでの実装例:ローディング状態の処理
NEWTでは非同期処理を多用するSPAのため、ローディング表示の待機パターンを用意しています。
public async waitForLoadingToComplete(): Promise<void> { const loadingText = this.page.getByText("読みこみ中です…"); const isLoadingVisible = await loadingText.isVisible().catch(() => false); if (isLoadingVisible) { await loadingText.waitFor({ state: "hidden", timeout: 30000 }); } }
このパターンは、Page Objectの基底クラスに実装しておくと、各テストから簡単に呼び出せます。自社プロジェクトでも同様のローディング待機メソッドを用意することを推奨します。
5.3 Page Object Model(POM)
考え方:POMを採用すべきプロジェクトの特性
以下のような特性を持つプロジェクトでは、POMの採用を推奨します。
特性 | POMのメリット |
複数画面で共通UIを使用 | コンポーネントを1箇所で定義し再利用 |
多段階のユーザーフロー | 各ステップの操作をカプセル化 |
UIの変更が頻繁 | 変更箇所が1箇所に集約 |
チームでの分業 | Page Object作成とテスト作成を分離可能 |
AIにPOMを遵守させるため、Generator Skillに以下のルールを埋め込んでいます。
## Page Object Model Requirements: - All page classes must extend `BasePage` - All component classes must extend `BaseComponent` - Static elements as direct properties - Dynamic elements as functions - Page navigation methods must return new page objects
基本的な実装パターン
1. 静的要素はプロパティとして定義
public searchButton = this.section.getByTestId("searchBox-submit"); public pageTitle = this.page.getByRole("heading", { name: "ページタイトル" });
2. 動的要素は関数として定義
public destinationButton = (label: string) => this.section.getByRole("button", { name: label }); public tourItem = (title: string) => this.page.getByRole("link", { name: title });
3. ページ遷移メソッドは新しいPage Objectを返す
public async goToSearchPage() { await this.searchBox.searchButton.click(); return new SearchPage(this.page); }
この「遷移先のPage Objectを返す」パターンにより、テストコードをメソッドチェーンで記述可能になります。
NEWTでの実装例:旅行予約フローへの適用
NEWTでは、複数アプリケーション(ツアー予約、ホテル予約など)で共通UIパターンを利用しています。
同じSearchBox、CalendarModal、OccupancyModalなどが複数アプリで使われており、POMなら共通コンポーネントを1箇所で定義し、各アプリのPage Objectで再利用できます。
TOP → 検索 → 一覧 → 詳細 → 予約入力 → 確認 → 決済 → 完了
これらの各ステップの操作とアサーションをPage Objectにカプセル化することで、テストコードがビジネスシナリオに集中できます。
// NEWTでの実装例 const tourTopPage = await new TourTopPage(page).open(); const searchPage = await tourTopPage.goToSearchPage(); const detailPage = await searchPage.selectTour("ハワイ5日間");
コンポーネント切り出しのヒント
Page Objectが肥大化した場合、以下の基準でコンポーネントクラスとして切り出しています。
- ファイルが大きくなってきた場合
- 要素ブロックの大きいものからコンポーネントクラスに切り出す
- モーダルの場合
- モーダルは画面から独立したコンテキストを持つ
- ファイル名はサフィックスとして
Modalを付ける(例:HotelDetailModal.ts)
5.4 認証パターン
考え方:認証パターンの分類
多くのWebアプリケーションでは、テストの種類に応じて複数の認証パターンを使い分ける必要があります。
パターン | ユースケース |
未ログイン状態 | サインイン/サインアップのテスト |
ログイン済み状態 | 一般的な機能テスト(デフォルト) |
特定の権限が必要 | 管理者機能、決済フローなど |
独立したユーザー | データ汚染を避けたいテスト |
Playwrightでは
storageState を使って認証済みの状態を保存・再利用できます。これにより、毎回ログイン操作を行う必要がなくなり、テストの実行時間を短縮できます。NEWTでの実装例:4つの認証パターン
NEWTでは、テストの種類に応じて4つの認証パターンを使い分けています。
// 1. 未ログイン状態(サインインテスト用) import { testNoAuth as test } from '@common/authUsingStorageState'; // 2. ログイン済み状態(通常テスト)- デフォルト import { test } from '@playwright/test'; // 3. GMO決済認証(決済フロー) import { testNeedGMOAuth } from '@common/authUsingStorageState'; // 4. Firebase認証(独立ユーザー) import { test } from '@fixtures/firebaseAuth';
認証選択フローチャート
テストの目的は? ├─ サインイン/サインアウトのテスト → testNoAuth ├─ GMO決済が含まれる → testNeedGMOAuth ├─ 一般的なログイン済み機能 → デフォルト(storageState) └─ 完全に独立した新規ユーザーが必要 → firebaseAuthPage
NEWTでの実装例:reCAPTCHAバイパス設定
NEWTではreCAPTCHAを利用しているため、テスト環境でreCAPTCHAをバイパスする必要があります。MCPでブラウザを操作する前に、このCookieを設定することが必須です。
// CRITICAL: Set firebase_testing cookie BEFORE navigation await page.context().addCookies([{ name: 'firebase_xxx_testing', value: 'xxx', domain: 'hoge', path: '/' }]); // Only AFTER setting cookie, navigate to the page await page.goto('<http://localhost:3000>');
この設定をプロンプトに明示することで、AIが生成するテストコードにも自動的に含まれるようになります。自社プロジェクトでも同様の認証バイパス設定がある場合は、プロンプトに含めることを推奨します。
5.5 PC/SP分離テスト
考え方:レスポンシブテストの原則
レスポンシブWebアプリケーションでは、PC(デスクトップ)とSP(スマートフォン)で表示やUI動作が異なります。
## Test Structure Requirements: - **ALWAYS create separate tests for PC and SP (mobile)** - Use `test.describe('PC', () => { ... })` for desktop tests - Use `test.describe('SP', () => { test.use({ viewport: { width: 375, height: 667 } }); ... })` for mobile - Pass `isMobile` parameter to Page Objects
NEWTでの実装例
test.describe('TC-1: サインイン', () => { // PC版テスト test.describe('PC', () => { test('未ログイン状態から電話番号でログイン', async ({ page }) => { const isMobile = false; const tourTopPage = new TourTopPage(page, isMobile); await tourTopPage.open(); // PC用のセレクター(ヘッダーのログインボタン) await page.getByTestId('header-xxx-button').click(); // ... }); }); // SP版テスト test.describe('SP', () => { test.use({ viewport: { width: 375, height: 667 } }); test('未ログイン状態から電話番号でログイン', async ({ page }) => { const isMobile = true; const tourTopPage = new TourTopPage(page, isMobile); await tourTopPage.open(); // SP用のセレクター(バナー) await page.getByRole('link', { name: '会員限定のおトクな情報をお届け 無料で会員登録' }).click(); // ... }); }); });
isMobileパラメータをPage Objectに渡すことで、Page Object内でも条件分岐が可能になります。5.6 コード生成優先ルール
考え方:AIのコード生成における判断基準
AIがコードを生成する際の判断優先順位を明確にしています。
## Code Generation Priority Rules ### Priority 1: Element Selectors & Robustness (HIGHEST) - 既存コードパターンから安定したセレクターを使用 - Click before fill パターン - Loading状態のハンドリング - `.first()` や `.nth()` で複数要素に対応 ### Priority 2: Flow & Navigation from MCP Behavior - MCPが観測したページフローを信頼 - 実際に動作したナビゲーション順序を採用 - 必要な中間ステップを省略しない ### Priority 3: Values & Test Data - 既存の定数ファイルを使用 - MCPで成功した値を適用 - ハードコードではなく定数をimport
Click before fill パターン
// 良い例 await page.getByTestId('login-phoneNumber-input').click(); await page.getByTestId('login-phoneNumber-input').fill('08000000001'); // 避けるべき例 await page.getByTestId('login-phoneNumber-input').fill('08000000001'); // → フォーカスが当たっていない状態でfillすると失敗することがある
NEWTでの実装例:定数ファイルの使用
NEWTでは、テストデータを
@constants/user.ts に集約しています。// 良い例 import { TEST_USER } from '@constants/user'; await page.fill('[data-testid="phone"]', TEST_USER.PHONE_NUMBER); // 避けるべき例 await page.fill('[data-testid="phone"]', '08000000001'); // → ハードコードは変更に弱い
自社プロジェクトでも同様に、テストデータを定数ファイルに集約し、AIに参照させることを推奨します。
6. 実践例:生成されたテストコード
以下は、Generator Skillが生成したサインインテストの実例です。
// spec: specs/signin-test.md import { testNoAuth as test } from '@common/authUsingStorageState'; import { expect } from '@playwright/test'; import { TourTopPage } from '@/pages/tour/TourTopPage'; import { TEST_USER } from '@constants/user'; test.describe('TC-1: サインイン', () => { test.describe('PC', () => { test('未ログイン状態から電話番号でログイン', async ({ page }) => { const isMobile = false; // Page Objectを使用してトップページを開く const tourTopPage = new TourTopPage(page, isMobile); await tourTopPage.open(); // ヘッダーからログイン(PC用のセレクター) await page.getByTestId('header-xxx-button').click(); // ログイン画面が表示されることを確認 await expect(page).toHaveURL(/\\\\/signin/); // 電話番号でログイン await page.getByRole('button', { name: '電話番号で続ける' }).click(); await page.getByTestId('login-phoneNumber-xxx').click(); await page.getByTestId('login-phoneNumber-xxx').fill(TEST_USER.PHONE_NUMBER); await page.getByTestId('login-phoneNumber-xxx').click(); // 認証コードを入力 await page.getByTestId('login-verificationCode-xxx').click(); await page.getByTestId('login-verificationCode-xxx').fill(TEST_USER.VALIDATION_CODE); await page.getByTestId('login-xxx-yyy').click(); // Loading待機処理 await expect(page.locator('text=Loading...').first()).not.toBeVisible(); // ログイン完了確認 await expect(page).toHaveURL('<http://localhost:3000/>'); }); }); test.describe('SP', () => { test.use({ viewport: { width: 375, height: 667 } }); test('未ログイン状態から電話番号でログイン', async ({ page }) => { const isMobile = true; // Page Objectを使用してトップページを開く const tourTopPage = new TourTopPage(page, isMobile); await tourTopPage.open(); // SPではバナーをクリック await page.getByRole('link', { name: '会員限定のおトクな情報をお届け 無料で会員登録' }).click(); // 以降は共通フロー... }); }); });
AI生成と手動調整の境界
AIが生成したコードの品質が高いため、レビュー時間が大幅に短縮されます。ゼロから書くのではなく、生成されたコードを確認・調整するワークフローです。
AIが自動生成した部分
- PC/SP分離の構造
- Page Objectの使用
- 定数(TEST_USER)のimport
- 基本的なフロー(クリック→入力→送信)
- Loading待機処理
人間がレビュー・調整した部分
- PC用とSP用のセレクターの選択(UIの違いに基づく判断)
- タイムアウト値の調整
- エッジケースの追加
7. 導入効果と限界、今後の展望
導入効果
検証の結果、Playwright Test AgentsやAIは強力ですが、それを既存プロジェクトにフィットさせるには、「ルールの注入」と「制御」がカギになることが分かりました。
- そのまま使うにはPlaywright Test Agents公式だけでは不十分だった
- プロジェクト固有のルールを注入する必要がある
- コーディング標準、Page Object、認証パターン等
- Generator Skillのプロンプト設計が品質を決める
- 詳細なプロンプトにより、一貫した品質のコードを生成
- 優先順位の明示、具体例の提示がポイント
- 要素選択、待機処理、テストデザインの3つが核心
- セマンティックロケーター優先
- sleep/wait禁止、明示的な条件待機
- Page Objectの使用強制
- サブエージェントよりSkillsが運用しやすい
- 自動判断による呼び出し
- チーム全体で一貫したルール適用
- バージョン管理による継続的改善
現時点での限界
- 複雑なビジネスロジックの判断: テストシナリオの設計自体は人間の判断が必要
- エッジケースの網羅: AIが自動的に境界値テストを設計するわけではない
今後の展望
- Healer Agentのカスタマイズによる自動修復の品質向上
- Planner Agentと既存Page Objectの連携
- テストカバレッジの自動分析との統合
Playwright Test Agents x Claude Code Skillsを試す最短ステップ
- 自社の標準を明文化する セレクター優先順位、待機処理、POM構成などを洗い出す。
- Generator Skillを作成する 本記事の構造を参考に、.claude/skills/ にプロンプトを配置する。
- 小さなテストから検証する まずはログインや検索など、単純なフローから生成・改善のサイクルを回す。
生成されたコードをレビューし、プロンプトを改善していく。
このサイクルを回すことで、プロジェクトに最適化されたGenerator Skillが完成します。
これらの学びはプロジェクトの性質に関わらず活用できるものも多いので、参考にしてみてください!
参考資料
📣 1月のイベント開催のお知らせ
令和トラベルでは、毎月技術的な知識や知見・成果を共有するLT会を毎月実施しています。発表テーマや令和トラベルに興味をお持ちいただいた方は、誰でも気軽に参加いただけます。
【1/28 開催!3社共催】モバイルアプリ開発 ✕ AI ー 組織・技術課題と向き合い、AIと走る
2026年のスタートを切る1月の「NEWT Tech Talk」は、”モバイルアプリ開発 ✕ AI ー 組織・技術課題と向き合い、AIと走る” というテーマで開催。
クラシル株式会社 なぐもさん、株式会社ヤプリ にゃふんたさんをゲストに、令和トラベル やぎにいの3名が登壇します。モバイルアプリ開発の現場で、AI活用に取り組む3社のエンジニアが、個人・チーム・技術課題それぞれの視点から、AIとどのように向き合い、どのように開発を前に進めてきたのか、具体の取り組みをシェアしながら語ります!
そのほか、毎月開催している技術発信イベントについては、connpass にてメンバー登録して最新情報をお見逃しなく!
【NEWT Chat リリース記念】AI × Travel Innovation Week 開催!
「NEWT Chat」誕生の裏側や開発ストーリーをお届けする特別企画 “AI × Travel Innovation Week” を令和トラベルのnote上で開催しました!
「NEWT Chat」のリリース背景、プロダクトの価値、開発体制、そして今後の展望など、新規事業の “舞台裏” を公開。特に、AIプロダクト開発に関わるエンジニア・PMの皆さまにとって学びの多い内容となりますので、ぜひご覧ください。
▼ AI × Travel Innovation Week のnoteはこちら:
旅行・観光業に特化したAIエージェントチャット「NEWT Chat(ニュートチャット)」についてはこちらから。
令和トラベルでは一緒に働く仲間を募集しています
この記事を読んで会社やプロダクトについて興味を持ってくれた方は、ぜひご連絡お待ちしています!お気軽にお問い合わせください!
フランクに話だけでも聞きたいという方は、カジュアル面談も実施できますので、お気軽にお声がけください。
1年間の感謝を込めた、”クリスマスセール🎄” 開催中!
NEWTでは現在、海外旅行やホテルをおトクにご予約いただける『クリスマスセール🎄』を12/4〜スタートしています!ぜひこの機会にご利用ください!
📣宣伝
次回の「NEWT Product Advent Calendar 2025」Day18は、「PMがAI x ローコードツール(Retool)で業務システムを作ってみた」と題してPMのrihoが担当します。次のブログもお楽しみに!





