Claude CodeでPlaywright E2Eテストを実務導入する方法
Claude CodeとPlaywrightでE2E、モバイル、認証、Trace Viewer、CIを実務レベルに整える。
Claude Codeに「Playwrightのテストを書いて」と頼むだけでは、公開前の安心材料にはなりません。動くことは動くけれど、CSSセレクタに依存してすぐ壊れる、ログインを毎回やり直して遅い、スマホのスクリーンショットを撮っていない、CIで落ちた理由がTrace Viewerで追えない。こうした状態だと、E2Eテストは品質を守る仕組みではなく、リリース前に気を重くする作業になります。
この記事では、Claude Codeを「テストを書くAI」ではなく「テスト設計を詰める相棒」として使い、PlaywrightのE2Eテストを実務に入れる手順を整理します。E2EはEnd to Endの略で、ユーザーがブラウザで行う操作を最後まで通すテストです。単体テストでは見えない、ルーティング、フォーム、認証、レスポンシブ表示、記事内CTA、外部APIの待ち方をまとめて確認できます。
公式情報は、Claude Code overview、Claude Code common workflows、PlaywrightのLocators、Authentication、Screenshots、Trace Viewer、Retries、CIを基準にしています。ClaudeCodeLab内では、テスト全体の位置づけをClaude Codeでテスト戦略を作る実践ガイド、CIの考え方をClaude CodeでCI/CDパイプラインを構築する完全ガイド、スマホ表示の崩れをClaude Codeでレスポンシブデザインを実装する実践ガイドと合わせて読むとつながります。
まず守る範囲を決める
E2Eテストは、すべての画面を細かく確認するための道具ではありません。ブラウザを起動するため、単体テストより遅く、失敗時の原因も広くなります。Claude Codeに任せる前に、守る範囲を絞るほど価値が上がります。
flowchart LR
A["収益や登録に近い導線"] --> B["PlaywrightでE2E化"]
C["表示崩れが売上に響く画面"] --> B
D["細かい計算やバリデーション"] --> E["Vitestなどの単体テスト"]
F["API境界やフォーム部品"] --> G["結合テスト"]
実務では次の3つから始めるのが扱いやすいです。
| ユースケース | 何を守るか | Playwrightで見るポイント |
|---|---|---|
| 記事から教材購入への導線 | /blog/... から /products/ へ進めるか | CTAリンク、URL、スマホでの押しやすさ |
| ログイン後の管理画面 | 認証済み状態で主要ページへ入れるか | storageState、権限、リダイレクト |
| コード記事のレイアウトQA | コードブロックや表がスマホで横にはみ出ないか | モバイルスクリーンショット、横スクロール、Trace |
この3つはClaudeCodeLabのような記事メディアでも、SaaSでも、ECでも応用できます。特に広告や教材販売で収益化しているサイトでは、「記事は読めるがCTAがスマホで隠れている」「コードブロックが本文幅を押し広げてCLSが悪化する」「ログイン済みの購入者ページだけCIで見ていない」といった小さな見落としが、そのまま機会損失になります。
Claude Codeへの依頼文を具体化する
Claude Codeはコードベースを読めますが、品質基準までは自動で決められません。最初の依頼では、対象URL、守る導線、使ってよいセレクタ、実行コマンド、触ってよいファイルを明示します。
既存のAstroサイトを読み、Playwright E2Eを追加してください。
目的:
- 記事 `/blog/claude-code-playwright-testing/` から `/products/` と `/training/` へ進めることを確認する
- 390px幅のモバイルで本文、表、コードブロックが横にはみ出さないことを確認する
- ログインが必要なテストは `storageState` を使い、毎回UIログインしない
- CIではretryを2回、traceは `on-first-retry` にする
制約:
- `page.waitForTimeout()` は使わない
- CSSクラス名だけのセレクタに依存しない
- 変更ファイルは `playwright.config.ts` と `tests/e2e/**` に限定する
- 実装後に `npx playwright test` を実行し、失敗したらTrace Viewerで原因を説明する
「テストを書いて」ではなく、どの失敗を検出したいかまで伝えるのが重要です。Claude Codeが提案したテストをレビューするときも、コード量より、セレクタ、待ち方、データ準備、失敗時の証跡を見ます。
最小構成をコピーして動かす
以下は既存アプリに追加できるPlaywright設定です。Astro、Next.js、Remixなどでも、BASE_URLと起動コマンドを変えれば流用できます。
cd site
npm i -D @playwright/test
npx playwright install
mkdir tests/e2e
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
const baseURL = process.env.BASE_URL ?? 'http://127.0.0.1:4321';
const hasAuth = Boolean(process.env.TEST_EMAIL && process.env.TEST_PASSWORD);
const authFile = 'playwright/.auth/user.json';
export default defineConfig({
testDir: './tests/e2e',
timeout: 30_000,
expect: { timeout: 5_000 },
fullyParallel: true,
forbidOnly: Boolean(process.env.CI),
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 2 : undefined,
reporter: process.env.CI ? [['html'], ['github']] : 'html',
use: {
baseURL,
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
...(process.env.PLAYWRIGHT_WEB_SERVER === '1'
? {
webServer: {
command: 'npm run preview -- --host 127.0.0.1 --port 4321',
url: baseURL,
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
}
: {}),
projects: [
...(hasAuth
? [
{
name: 'setup',
testMatch: /.*\.setup\.ts/,
},
]
: []),
{
name: 'desktop-chrome',
use: {
...devices['Desktop Chrome'],
storageState: hasAuth ? authFile : undefined,
},
dependencies: hasAuth ? ['setup'] : [],
},
{
name: 'mobile-safari',
use: {
...devices['iPhone 13'],
storageState: hasAuth ? authFile : undefined,
},
dependencies: hasAuth ? ['setup'] : [],
},
],
});
ポイントは、ローカルではretryを0にし、CIだけretryを有効にすることです。ローカルで失敗が再現するなら、すぐ直すほうが速いからです。一方、CIではCPUやネットワークが不安定になるため、retryとTrace Viewerを組み合わせて、偶発的な失敗と本物の不具合を切り分けます。
認証状態はstorageStateで保存する
ログインが必要なE2Eを毎回UIから始めると、遅く、壊れやすく、二要素認証やbot対策にも引っかかります。PlaywrightのstorageStateは、認証済みのCookieやlocalStorageをファイルに保存し、後続テストで再利用する仕組みです。保存ファイルには機密情報が入るため、playwright/.authは必ず.gitignoreに入れます。
// tests/e2e/auth.setup.ts
import { test as setup, expect } from '@playwright/test';
import fs from 'node:fs';
import path from 'node:path';
const authFile = path.resolve('playwright/.auth/user.json');
const email = process.env.TEST_EMAIL;
const password = process.env.TEST_PASSWORD;
setup('save signed-in browser state', async ({ page }) => {
setup.skip(!email || !password, 'Set TEST_EMAIL and TEST_PASSWORD to record auth state.');
await page.goto('/login');
await page.getByLabel(/email|メール|e-mail/i).fill(email!);
await page.getByLabel(/password|パスワード/i).fill(password!);
await page.getByRole('button', { name: /log in|sign in|ログイン/i }).click();
await expect(page).toHaveURL(/dashboard|account|admin/);
await expect(page.locator('body')).toBeVisible();
fs.mkdirSync(path.dirname(authFile), { recursive: true });
await page.context().storageState({ path: authFile });
});
ここでもClaude Codeに丸投げせず、「テストアカウントは本番ユーザーと分ける」「保存ファイルはコミットしない」「権限の強い管理者アカウントをCIに置かない」と条件を渡します。認証はE2Eの便利さとセキュリティリスクが隣り合わせです。
モバイルスクリーンショットとコードブロックをQAする
ClaudeCodeLabの記事では、コードブロック、表、CTAが本文の中心です。PCではきれいでも、390px幅でコードブロックが画面を押し広げたり、長いURLがカードを突き破ったりします。Playwrightなら、モバイル幅に固定してスクリーンショットを残し、横方向のはみ出しを数値で検出できます。
// tests/e2e/article-quality.spec.ts
import { test, expect } from '@playwright/test';
const articlePath = process.env.ARTICLE_PATH ?? '/blog/claude-code-playwright-testing/';
test.describe('article quality checks', () => {
test('article has monetization CTAs', async ({ page }) => {
await page.goto(articlePath);
await expect(page.getByRole('heading', { level: 1 })).toContainText(/Playwright|E2E|Claude Code/i);
await expect(page.locator('a[href="/products/"], a[href="/products"]').first()).toBeVisible();
await expect(page.locator('a[href="/training/"], a[href="/training"]').first()).toBeVisible();
});
test('mobile layout has no horizontal overflow', async ({ page }, testInfo) => {
await page.setViewportSize({ width: 390, height: 844 });
await page.goto(articlePath);
await expect(page.locator('main, article').first()).toBeVisible();
const overflow = await page.evaluate(() => ({
viewport: window.innerWidth,
documentWidth: document.documentElement.scrollWidth,
offenders: Array.from(document.querySelectorAll('pre, table, img, iframe, .prose'))
.filter((node) => {
const rect = node.getBoundingClientRect();
return rect.left < -1 || rect.right > window.innerWidth + 1;
})
.map((node) => {
const rect = node.getBoundingClientRect();
return `${node.tagName.toLowerCase()} ${Math.round(rect.left)}-${Math.round(rect.right)}`;
}),
}));
expect(overflow.documentWidth, JSON.stringify(overflow)).toBeLessThanOrEqual(overflow.viewport + 2);
expect(overflow.offenders).toEqual([]);
await page.screenshot({ path: testInfo.outputPath('article-mobile.png'), fullPage: true });
});
test('code examples are present and copyable', async ({ page }) => {
await page.goto(articlePath);
const blocks = page.locator('pre code');
await expect(blocks.first()).toBeVisible();
expect(await blocks.count()).toBeGreaterThanOrEqual(3);
await expect(blocks.nth(0)).toContainText(/playwright|defineConfig|test/i);
});
});
このテストは「見た目」を画像だけに頼らず、横スクロールの発生を数値で落とします。スクリーンショットはレビュー用、数値チェックはCI用と役割を分けると、PRで説明しやすくなります。
収益導線をE2Eに入れる
AdSenseや教材販売を考えるなら、記事の本文品質だけでなく、読者が次に進める導線もテスト対象です。広告審査ではコンテンツの独自性が重要ですが、サイト運営ではCTAが壊れていないことも同じくらい現実的な品質です。
// tests/e2e/revenue-flows.spec.ts
import { test, expect } from '@playwright/test';
const articlePath = process.env.ARTICLE_PATH ?? '/blog/claude-code-playwright-testing/';
test.describe('revenue and learning flows', () => {
test('reader can move from article to products', async ({ page }) => {
await page.goto(articlePath);
await page.locator('a[href="/products/"], a[href="/products"]').first().click();
await expect(page).toHaveURL(/\/products\/?$/);
await expect(page.locator('main').first()).toBeVisible();
});
test('training CTA is reachable on mobile', async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await page.goto(articlePath);
await page.locator('a[href="/training/"], a[href="/training"]').first().click();
await expect(page).toHaveURL(/\/training\/?$/);
await expect(page.locator('main').first()).toBeVisible();
});
test('main navigation can open the blog index', async ({ page }) => {
await page.goto('/');
await expect(page.getByRole('navigation').first()).toBeVisible();
await page.getByRole('link', { name: /blog|記事|articles/i }).first().click();
await expect(page).toHaveURL(/blog/);
});
});
Claude Codeにこのテストを改善させるなら、「CTAが複数ある場合は最初の1つだけでよいか」「外部決済ページへ進む場合はどこまでテストするか」「広告やABテストでリンクが変わる場合にdata-testidを付けるか」を相談します。収益導線のE2Eは、細かく書きすぎると壊れやすく、浅すぎると価値がありません。
セレクタはユーザー視点で選ぶ
Playwrightの強みはLocatorです。Locatorは要素をその時点で探し、操作前に自動待機します。Claude Codeが.card:nth-child(3) .btnのようなCSSセレクタを出してきたら、まず疑ってください。見た目の都合でクラス名や順番が変わると壊れるからです。
| 優先度 | 使うセレクタ | 例 | 理由 |
|---|---|---|---|
| 高 | roleとname | page.getByRole('button', { name: /save/i }) | アクセシビリティと近い |
| 高 | label | page.getByLabel(/email/i) | フォームの意味を確認できる |
| 中 | text | page.getByText(/購入する/) | 文言変更に注意 |
| 中 | test id | page.getByTestId('checkout-submit') | UI文言が頻繁に変わる箇所向き |
| 低 | CSS構造 | .card:nth-child(3) | レイアウト変更で壊れやすい |
data-testidは逃げではありません。決済、ログアウト、ドラッグ操作のように、見た目や翻訳で文言が変わる場所では安定した契約になります。ただし、すべてに付けるとテストが実装に寄りすぎます。Claude Codeには「まずroleとlabel、難しい場所だけtest id」と指示すると、レビューしやすい差分になります。
Trace Viewerで失敗を読む
CIでPlaywrightが落ちたとき、ログだけでは画面の状態がわかりません。Trace Viewerを使うと、操作ごとのDOMスナップショット、スクリーンショット、ネットワーク、コンソールログを追えます。trace: 'on-first-retry'にしておくと、全テストで重いトレースを残さず、失敗したテストの再試行時だけ証跡を保存できます。
npx playwright test --trace on
npx playwright show-report
npx playwright show-trace test-results/path-to-trace/trace.zip
Claude Codeに失敗を渡すときは、単に「直して」ではなく、次の3点を貼ります。
- 失敗したテスト名
- Trace Viewerで見えた直前の画面状態
- 期待していたユーザー行動
これでClaude Codeは、待ち時間を伸ばすだけの修正ではなく、セレクタの改善、APIモック、データ準備、UI側のアクセシビリティ修正を提案しやすくなります。
CIではretryと成果物保存をセットにする
CIのretryは、 flaky testを隠すためではありません。Playwrightはretryで通ったテストをflakyとして分類します。つまり、「失敗したが再実行で通った」事実を残せます。重要なのは、retryを入れるならTrace、HTMLレポート、スクリーンショットを必ず保存することです。
# .github/workflows/playwright.yml
name: Playwright E2E
on:
pull_request:
push:
branches: [main]
jobs:
e2e:
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: site
env:
BASE_URL: http://127.0.0.1:4321
PLAYWRIGHT_WEB_SERVER: "1"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: site/package-lock.json
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run build
- run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: site/playwright-report
retention-days: 7
この設定では、ローカルとCIで同じテストを使いながら、CIだけretry、Trace、レポート保存を厚くしています。waitForTimeoutで待つテストが増えるとCI時間が膨らむため、expect(locator).toBeVisible()やtoHaveURL()のようなWeb-first assertionを優先します。
よくある落とし穴
1つ目は、スクリーンショットだけで合否を決めることです。画像比較は便利ですが、フォント差、広告枠、日付、アニメーションで揺れます。記事レイアウトでは、まず横スクロールやCTAの表示を数値で検出し、画像はレビュー資料として使うほうが安定します。
2つ目は、ログインを毎テストに含めることです。ログイン画面のテストは必要ですが、すべてのテストがログインUIに依存すると、1箇所の変更で全E2Eが落ちます。認証後の機能確認はstorageStateへ分けます。
3つ目は、retryで問題を消したつもりになることです。retryで通ったテストは「不安定なまま通った」だけです。週に一度でもflakyが出るテストは、Traceを見て、ネットワーク待ち、時刻依存、ランダムデータ、CSSアニメーション、セレクタの弱さを切り分けます。
4つ目は、Claude Codeに修正範囲を渡さないことです。テストを追加するだけのつもりが、UIコンポーネントやルーティングまで大きく変わることがあります。実務では「まず失敗するテストだけ追加」「次に最小修正」「最後に検証コマンド」という順番を守ると、レビューが軽くなります。
収益化につなげる実装順
ClaudeCodeLabでこの流れを導入するなら、最初は記事テンプレート、CTA、コードブロック、ログイン済み教材ページの4点に絞ります。自分で試す場合はproductsのテンプレート集でプロンプトを整え、チームで導入する場合はtrainingでレビュー基準とCI運用をそろえると、テストが個人の努力に閉じません。
公開前チェックでは、記事本文の独自性、公式リンク、内部リンク、モバイル表示、CTA到達、Trace付きCIを1つのチェックリストにします。AdSense向けには、単なるツール紹介ではなく「どの失敗を検出し、どう直したか」を記事に残すほうが価値になります。読者も、コードを貼るだけでなく、失敗時に何を見るかまで持ち帰れます。
この記事で紹介した構成をClaudeCodeLabのローカル記事ページに当て、390px幅のスクリーンショット、コードブロックの横はみ出し、/products/と/training/へのCTA遷移、CI向けretry設定を順番に確認しました。実際に試した結果、最初に見つかりやすい不具合はテストコードではなく、長いコード行と曖昧なリンク名でした。そこを直してからTrace Viewerを残すようにすると、Claude Codeへの追加依頼も「どこが壊れたか」から始められます。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
Claude Code Permission Receipt Pattern: 許可、証拠、ロールバックを残す運用
Claude Codeの権限運用を安全にする permission receipt。許可範囲、承認待ち、検証コマンド、CTA導線を記録します。
Claude CodeとCodex、結局どっち?事故らない“併用”の現実解
OpenAIのCodexとClaude Code、どっちが得意でどっちに任せる?両方を安全に併用する作業分担と権限・検証のワークフローを、僕の失敗談つきで解説します。
Claude Codeサブエージェント実装ガイド: 記事・コード作業を安全に並列委譲する方法
Claude Codeサブエージェントで記事・コード作業を安全に並列化する実装ガイド。委譲基準、プロンプト、失敗例を解説。