こんにちは、hacomonoQA部のたっきー(滝田)です。
hacomonoのQA部では自動テストの手段の1つにPlaywrightを利用しています。
今回は、Claude Codeを用いたEnd-to-End(E2E)テストの実装について紹介します。
はじめに
本記事の執筆サポートとしてClaude Codeを利用しております。
E2Eテストの実装、テックブログに向けて実装内容の整理、記事の下書きにもClaude Codeを用いております。
プロジェクト概要
対象システムは、会員管理、予約システム、決済処理、外部デバイス連携など、多岐にわたる機能を提供するWebアプリケーション「hacomono」です。
E2Eテストの技術スタック
- テストフレームワーク: Playwright
- 言語: TypeScript
- CI/CD: GitHub Actions
- 開発支援: Claude Code
プロンプト
PlaywrightでE2Eテストを実装してください。 実装のステップは以下に従うこと。 1. 既存のプロジェクトの構造や内容を参照する。xxにはAPI Requestに関する情報が記述されています。yyにはテストが実装されている。 2. テスト目的は「会員の契約プラン変更機能が正常に動作するか」である。 3. beforeEachで会員情報やプラン情報の新規登録を行う 4. testでテスト目的に該当する操作を実装する 5. afterEachには本テスト実行によって作成されるテストデータの削除を実装する 6. 繰り返し同じコードを記述することは避け、共通処理や新規メソッドを作成すること
Claude Codeとの協働による開発フロー
1. テスト設計フェーズ
Claude Codeは、複雑なビジネスロジックを理解し、適切なテストシナリオを提案します。例えば、会員のプラン変更機能のテストでは以下のような包括的なテストケースが生成されました:
test.describe('管理画面: 会員プラン変更機能', () => { test('管理画面でプラン変更が実行できること', async ({ page }) => { await test.step('会員詳細画面に遷移する', async () => { await navigate(page, '会員管理', '会員一覧') await page.getByRole('textbox', { name: 'キーワード検索' }).fill(memberInfo.email) await clickSearchButton(page) await page.getByRole('link', { name: memberInfo.name }).click() }) await test.step('プラン変更手続きを開始する', async () => { await page.getByRole('button', { name: 'プラン変更' }).click() await page.getByText('プラン変更設定').waitFor({ state: 'visible' }) }) await test.step('新しいプランを選択する', async () => { await selectFromDropdown(page, 'プラン', newPlan.name) }) await test.step('プラン変更を保存する', async () => { page.on('dialog', (dialog) => dialog.accept()) await page.getByRole('button', { name: '変更を適用' }).click() await expect(page.getByText('変更が完了しました')).toBeVisible() }) }) })
2. 実装フェーズ
構造化されたテストアーキテクチャ
Claude Codeは、保守性の高いテストコードの構造を提案しました:
// fixtures/pagesFixture.ts - 再利用可能なページオブジェクト export const test = base.extend<PageFixtures>({ adminPage: async ({ browser }, use) => { const context = await browser.newContext() const page = await context.newPage() const adminPage = await admin.open(page) await use(adminPage) await page.close() await context.close() }, userPage: async ({ browser }, use) => { const context = await browser.newContext() const page = await context.newPage() const userPage = await user.open(page) await use(userPage) await page.close() await context.close() }, })
APIリクエストヘルパーの実装
テストデータの準備を効率化するため、APIリクエストヘルパーが実装されました:
// lib/requests/api/plan-helper.ts export const createTestPlan = async (adminPage: AdminPage, options: PlanOptions) => { const response = await adminPage.request.post('/api/plans', { data: { name: `テストプラン_${dateTime()}`, plan_type: options.type, location_ids: options.locationIds, // ... その他の設定 } }) return response.json() }
3. 品質保証フェーズ
Visual Regression Testing(VRT)
UI の視覚的な回帰を検出するため、VRTテストも実装されました:
test.describe('管理画面: 会員プラン変更機能', { tag: ['@vrt'] }, () => { test('会員の契約プラン画面に遷移する', async ({ adminPage }) => { await expect(page).toHaveScreenshot('member_plan_page.png', { mask: [page.locator('div.sidebar'), page.locator('header'), page.locator('div.main-content')] }) }) })
実装成果と効果
開発効率の向上
Claude Codeとの協働により、以下の効果が得られました:
- テスト作成時間の短縮: 従来の50%の時間でテストケースを作成
- コード品質の向上: 一貫性のある、保守性の高いテストコード
- バグの早期発見: 包括的なテストケースにより、本番環境での問題を大幅に削減
具体的な改善点
- テストの構造化: 複雑なビジネスロジックを段階的にテストする構造
- 再利用可能なコンポーネント: 共通処理をヘルパー関数として分離
- エラーハンドリング: 適切な待機処理とエラー対応
- 保守性: 自己文書化されたテストコード
学んだベストプラクティス
1. AI支援開発の効果的な活用
- 具体的な要件を明確化: Claude Codeに対して、テストの目的と期待する動作を具体的に伝える
- 段階的な実装: 大きな機能を小さな単位に分割して実装
- 継続的なレビュー: 生成されたコードの品質を人間が確認・改善
2. E2Eテストの設計原則
- 独立性の確保: 各テストが他のテストに依存しない構造
- データの隔離: テスト用データの作成・削除を適切に管理
- 安定性の追求: 非同期処理への適切な対応
今後の展望
Claude Codeを活用したE2Eテスト開発により、以下の取り組みを進めています:
- テストカバレッジの拡大: より多くの機能に対するテストケースの作成
- 自動化の推進: CI/CDパイプラインでの自動テスト実行
- E2Eテスト開発の高速化と効率化
まとめ
Claude Codeとの協働により、E2Eテストの開発効率を大幅に向上させることができました。
人間がE2Eテストの目的と期待する動作を定義し、実装フェーズではAIの支援を受けることで実装時間の大幅な短縮を実現しました。
今回は、現在hacomonoのE2Eテスト実装にあたりベストプラクティスを参照するように指示を出しました。また、一度の指示で実装は完結しておらず、人間のレビューによって細かな修正点を発見して修正指示を数度重ねることでテスト実装が完了しております。
現在は、このようなルールやベストプラクティスを定義することで、プロンプトに明記せずに同品質のE2Eテストが実装できる状態に近づいています。
hacomono QAではQAエンジニアを積極的に採用しています。
ぜひ気軽にお問い合わせやカジュアル面談を申し込みください。
💁 関連記事