テストコードが一本もないプロジェクトで、リリースのたびに丸1日かけて手動確認していた時期がある。今はその検証が20分で終わる。差分を埋めたのはテスト戦略の刷新ではなく、Claude Codeに「テストプロセスのどの段階を、どう任せるか」を1つずつ設計し直したことだった。
個人でLaravel・Rails案件をいくつか回す中で、テスト生成からブラウザ操作の自動化、CI/CDまでQAに関わる作業をClaude Codeに委任してきた。振り返ると場当たり的にやっていたわけではなく、5つのパターンに整理できることに気づいた。この記事では、その5パターンを「状況→行動→結果→学び」の形で棚卸しする。
前提
- Laravel(PHPUnit)とRailsが混在する個人開発・小規模受託案件が対象
- Claude CodeのHooks機能(
settings.jsonのhooksキー)を利用 - E2EはPlaywright、CI/CDはGitHub Actionsを使用
- チーム規模は基本1人〜数人(レビュー担当が自分しかいない前提の工夫が多い)
パターン1: Hooksでコード変更のたびに自動でリント・テストを走らせる
状況: Claude Codeが変更したファイルを、毎回自分の目で確認してからテストコマンドを手打ちしていた。地味に時間を食ううえに、疲れている日は「あとでまとめて」と後回しにして、結局テストを回さないままコミットすることがあった。
行動: PostToolUseフックで Edit / Write によるファイル変更のたびにリントとテストを自動実行するようにした(Bash経由の変更は対象外)。加えて、危険なコマンドを止めるPreToolUseフック(いわゆるkillスイッチ)も併用している。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/lint-and-test.sh",
"timeout": 120
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/kill-switch.sh"
}
]
}
]
}
}
結果: コード変更のたびに自動で品質チェックが走るようになり、構文エラーやテスト失敗の見逃しがゼロになった。
学び: Hooksは「AIの行動に自動チェックポイントを設ける」仕組みで、私の使い方ではCI/CDの手前に置く簡易なチェックとして機能している。人間のレビュー待ちにせず、変更の瞬間にフィードバックが返ってくるのが効いている。
パターン2: PHPUnitのテスト一括生成でゼロから土台を作る
状況: あるLaravelプロジェクトで、大量のAPIエンドポイントにテストが一本もない状態だった。手動でゼロから書くには気が遠くなる量で、品質担保ができていないまま機能追加だけが進んでいた。
行動: 既存のコントローラーとモデルをClaude Codeに読み込ませ、CLAUDE.mdにテストの命名規約とアサーション方針を明記したうえでPHPUnitテストを一括生成させた。
結果: 生成されたテストの大半はそのまま通ったが、業務ロジックの境界値まわり(割引率の上限・在庫がゼロになる境界など)は自分で手直しが必要だった。ゼロの状態から「動くテストの土台」ができるまでの時間は、手書きの見積もりと比べて大幅に短縮できた。
学び: テスト生成はClaude Codeの得意領域だが、任せきりにはできない。ビジネスロジックの境界値テストは、仕様を知っている人間が最後に補完する仕事として残る。
パターン3: Feature Testで「重要な業務フロー」から守る
テストゼロの状態から一気に全部カバーしようとすると、たいてい途中で挫折する。優先順位を付けたのがこのパターン。
状況: 別のLaravelプロジェクトで、テストコードがゼロの状態のまま機能追加を繰り返しており、リリースのたびに手動テストで丸1日かかっていた。
行動: まず業務上重要なフロー(注文・請求・在庫更新など、売上に直結する処理)のFeature Testを30ケース作成した。Factoryでテストデータ生成を効率化し、CI/CDパイプラインに組み込んだ。
public function test_order_reduces_stock_and_creates_invoice(): void
{
$product = Product::factory()->create(['stock' => 10]);
$user = User::factory()->create();
$this->actingAs($user)
->postJson('/api/orders', [
'product_id' => $product->id,
'quantity' => 3,
])
->assertCreated();
$this->assertEquals(7, $product->fresh()->stock);
$this->assertDatabaseHas('invoices', ['user_id' => $user->id]);
}
結果: リグレッションバグが月3件からゼロに減少。リリース前テストが1日から20分に短縮された。
学び: テストゼロの状態からでも、業務上重要なフローから優先的にテストを書けば投資対効果が高い。全部を一度にやろうとしないのがコツ。
パターン4: Playwrightでブラウザ操作そのものをテストにする
状況: 実はPlaywrightを最初に使ったのはQA目的ではなく、自治体への報告書提出フォームに毎月50項目を手入力する作業をRPA的に自動化するためだった。CSVから値を読み込んでフォームに自動入力し、送信前に画面キャプチャで確認するスクリプトを組んだところ、入力作業にかかっていた時間が10分程度まで圧縮できた。ただしこのとき、サイト側のUI変更でCSSセレクタ指定のスクリプトが何度も壊れる経験もした。
行動: この「data属性で要素を指定すればUI変更に強くなる」という教訓を、そのままE2Eテストに転用した。ログイン〜購入完了までの一連の操作をPlaywrightのテストとして書き、CIで毎回実行するようにしている。
import { test, expect } from '@playwright/test';
test('ログインしてカート内商品を購入できる', async ({ page }) => {
await page.goto('/login');
await page.getByTestId('email-input').fill('qa-user@example.com');
await page.getByTestId('password-input').fill('test-password');
await page.getByTestId('login-button').click();
await page.getByTestId('checkout-button').click();
await expect(page.getByTestId('order-complete-message')).toBeVisible();
});
結果: 手作業の確認では見落としていたログイン〜購入完了までの一連の導線が、コード変更のたびに自動で検証されるようになった。
学び: ブラウザ自動化の技術基盤はRPAでもQAでも変わらない。一方で使う目的が違うだけで得られる教訓は共通しており、「セレクタは壊れにくい指定を選ぶ」という地味な設計判断が、後から効いてくる。
パターン5: CI/CDパイプラインに丸ごと組み込む
状況: 個人開発の案件で、テストの実行とデプロイを手動で行っており、テスト実行忘れやデプロイ手順のミスが四半期に1回発生していた。
行動: GitHub Actionsでパイプラインを構築し、プッシュ時に自動テスト、mainブランチへのマージ時にステージングデプロイ、タグ作成時に本番デプロイが走る仕組みにした。
name: ci
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: composer install --no-interaction
- run: php artisan test
- run: npx playwright install --with-deps
- run: npx playwright test
結果: テスト実行忘れとデプロイ手順のミスがゼロになった。デプロイ頻度も月2回から週2回に増え、小さな改善を素早くリリースできるようになった。
学び: CI/CDを入れてから、品質ゲートが毎回必ず走るようになり、リリースまでの手数も減った。パターン1〜4で作った自動チェックは、CIに載せて初めて「毎回必ず実行される」ものになる。
5パターンのチートシート
| パターン | 何を任せるか | 実行タイミング | 前提条件 |
|---|---|---|---|
| 1. Hooks自動チェック | リント・テストの自動実行 | ファイル変更の直後 |
settings.jsonにHooks設定 |
| 2. テスト一括生成 | ゼロからのPHPUnit土台作成 | プロジェクト立ち上げ時 | CLAUDE.mdに命名規約・アサーション方針を明記 |
| 3. Feature Test運用 | 重要業務フローの優先カバー | 機能追加の都度 | Factoryでテストデータ整備 |
| 4. Playwright E2E | ブラウザ操作の自動検証 | CI実行時 | data属性でのセレクタ指定 |
| 5. CI/CD組み込み | テスト+デプロイの自動化 | push/merge/tag作成時 | GitHub Actionsワークフロー |
まとめ
- Hooksは「変更の瞬間」、Feature Testは「重要フローの優先順位」、Playwrightは「ブラウザ操作そのもの」、CI/CDは「毎回必ず実行される仕組み」を担当していて、それぞれ役割が違う
- テスト生成を任せても、境界値ロジックの妥当性は人間の仕事として残る
- RPAで得た「セレクタの壊れにくさ」のような教訓は、目的が違うQAの文脈にもそのまま転用できる
結局のところ、いちばん効いたのは「Claude Codeに何を任せるか」を1つのパターンに固定しなかったことだった。テスト生成・自動チェック・ブラウザ操作・パイプラインは別々の性質を持つ作業で、それぞれに合った任せ方をパターンとして持っておくと、新しい案件でもどこから手をつけるか迷わなくなる。
正直、パターン4のPlaywrightは最初サイト側のUI変更のたびにテストが落ちて、何度か原因調査に時間を溶かしました。同じところでハマっている人が遠回りせずに済めば、この記事を書いた意味があります。似た経験や「うちはこう回避した」があれば、ぜひコメントで共有してください。