Claude Codeでテストを自動生成する

2026/07/02
森正

こんにちは。株式会社リンクネット ソリューション事業部の森正です。

弊社では Claude Code を使った開発のなかで、テストコードの作成も Claude Code に任せることで効率化を図っています。

今回は Laravel 12 で作成したタスク管理アプリを題材に、Unit・Feature・E2E(Playwright)の3層すべてを Claude Code に生成させる流れを紹介します。


1. サンプルプロジェクトの概要

題材は、Laravel 12 + React + Inertia.js で構成したタスク管理アプリです。

タスク一覧画面

タスクの作成・編集はダイアログで行います。

タスク作成ダイアログ

機能は、ユーザー認証(Laravel Breeze)、タスクの作成・参照・更新・削除、ステータス管理(未着手・進行中・完了)とそのフィルタリングを備えています。

最終的に生成したテストは、以下のような構成になりました。

tests/
├── Unit/                    # Unit テスト
│   └── Models/TaskTest.php
├── Feature/                 # Feature テスト
│   └── Tasks/
│       ├── TaskCreateTest.php
│       ├── TaskUpdateTest.php
│       ├── TaskDeleteTest.php
│       └── TaskIndexTest.php
└── e2e/                     # E2Eテスト (Playwright)
    ├── helpers/auth.ts
    └── tasks/task-crud.spec.ts

2. まず CLAUDE.md にテスト方針を書く

いきなりテストを書かせる前に、やっておくと良いのが CLAUDE.md への方針の明記です。

ここにテストの規約を書いておくと、毎回プロンプトで細かく指示しなくても、一貫したスタイルのテストが出てくるようになります。

# テスト方針

## PHPUnit
- テストクラスには `use RefreshDatabase` を使用する
- テストメソッドには `#[Test]` アトリビュートを付与する
- メソッド名は英語のスネークケースで、テスト対象の振る舞いを説明する
- Factory を使用してテストデータを生成する
- 認証が必要なテストは `actingAs($user)` を使用する

## Playwright (E2E)
- テストファイルは `tests/e2e/` 配下に配置する
- 要素の特定には `data-testid` 属性を使用する
- ログインが必要なテストは `helpers/auth.ts``login()` 関数を使用する

## テスト実行コマンド
- PHPUnit: `./vendor/bin/sail test`
- Playwright: `./vendor/bin/sail npx playwright test`

3. Unit テスト ── ロジックの正しさを確かめる

まずはモデルの Unit テストです。

次のように指示します。

Task モデルのユニットテストを作成してください。
リレーション、Enumキャスト、スコープのテストを含めてください。

生成されたのが次のテストです。

モデルの定義を読み取り、検証すべき内容を自動で抽出しています。

<?php

namespace Tests\Unit\Models;

use App\Enums\TaskStatus;
use App\Models\Task;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

class TaskTest extends TestCase
{
    use RefreshDatabase;

    #[Test]
    public function it_belongs_to_a_user(): void
    {
        $task = Task::factory()->create();

        $this->assertInstanceOf(User::class, $task->user);
    }

    #[Test]
    public function it_casts_status_to_enum(): void
    {
        $task = Task::factory()->todo()->create();

        $this->assertInstanceOf(TaskStatus::class, $task->status);
        $this->assertSame(TaskStatus::Todo, $task->status);
    }

    #[Test]
    public function scope_status_filters_correctly(): void
    {
        $user = User::factory()->create();
        Task::factory()->count(3)->todo()->for($user)->create();
        Task::factory()->count(2)->done()->for($user)->create();

        $this->assertCount(3, Task::status(TaskStatus::Todo)->get());
        $this->assertCount(2, Task::status(TaskStatus::Done)->get());
    }
}

リレーション・Enumキャスト・カスタムスコープと、検証すべき観点を適切に押さえています。

$ ./vendor/bin/sail test tests/Unit/Models/TaskTest.php

   PASS  Tests\Unit\Models\TaskTest
  ✓ it belongs to a user
  ✓ it casts status to enum
  ✓ scope status filters correctly

  Tests:    3 passed (7 assertions)

4. Feature テスト ── 観点を箇条書きで渡す

Feature テストでは、API レベルの動作を確認します。

ここでのポイントは、確認したい観点を箇条書きで渡すことです。

これにより、Claude Code が漏れなくテストケースに反映します。

TaskController の作成・参照・更新・削除操作に対するフィーチャーテストを作成してください。
以下の観点を含めてください:
- 認証チェック
- 正常系(作成・更新・削除・一覧表示)
- 認可チェック(他ユーザーのタスクは操作できない)
- バリデーション
- ステータスフィルタリング

生成された作成テストの抜粋です。

<?php

namespace Tests\Feature\Tasks;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

class TaskCreateTest extends TestCase
{
    use RefreshDatabase;

    #[Test]
    public function guest_cannot_access_create_form(): void
    {
        $this->get(route('tasks.create'))->assertRedirect(route('login'));
    }

    #[Test]
    public function user_can_create_a_task(): void
    {
        $user = User::factory()->create();

        $response = $this->actingAs($user)->post(route('tasks.store'), [
            'title' => 'テストタスク',
            'description' => 'テストの説明',
            'status' => 'todo',
        ]);

        $response->assertRedirect(route('tasks.index'));
        $this->assertDatabaseHas('tasks', [
            'title' => 'テストタスク',
            'user_id' => $user->id,
        ]);
    }

    #[Test]
    public function title_is_required(): void
    {
        $user = User::factory()->create();

        $this->actingAs($user)
            ->post(route('tasks.store'), ['title' => '', 'status' => 'todo'])
            ->assertSessionHasErrors('title');
    }
}

Controller のルーティング、FormRequest のバリデーションルール、Policy の認可ロジックまで読み取ったうえで、観点ごとのテストケースを作成しています。

$ ./vendor/bin/sail test tests/Feature/Tasks/

   PASS  Tests\Feature\Tasks\TaskCreateTest
  ✓ guest cannot access create form
  ✓ user can create a task
  ✓ title is required
  ✓ title max length is 255
  ✓ status must be valid enum value
  ...

  Tests:    17 passed (58 assertions)

5. E2Eテスト(Playwright)

E2E には Playwright を使用します。

まずは環境のセットアップから依頼します。

Playwright のセットアップをしてください。
Laravel Sail 環境で動作するようにしてください。

設定では、テスト前にDBをリセットして再シードする globalSetup も生成されます。

// playwright.config.ts
export default defineConfig({
    testDir: './tests/e2e',
    workers: 1,
    use: {
        baseURL: 'http://localhost',
        screenshot: 'only-on-failure',
    },
    globalSetup: './tests/e2e/global-setup.ts',
});

ログインのような共通処理は、ヘルパーとして切り出して生成されます。

// tests/e2e/helpers/auth.ts
import { Page } from '@playwright/test';

export async function login(page: Page, email = 'test@example.com', password = 'password') {
    await page.goto('/login');
    await page.locator('#email').fill(email);
    await page.locator('#password').fill(password);
    await page.getByRole('button', { name: 'ログイン' }).click();
    await page.waitForURL('**/dashboard', { timeout: 10000 });
}

そのうえで一連の操作の E2E を依頼します。

タスクの作成・参照・更新・削除操作に対する E2E テストを Playwright で作成してください。
import { test, expect } from '@playwright/test';
import { login } from '../helpers/auth';

test.describe('Task management', () => {
    test.beforeEach(async ({ page }) => {
        await login(page);
    });

    test('user can create a new task', async ({ page }) => {
        await page.goto('/tasks');
        await page.click('[data-testid="create-task-button"]');

        await page.fill('[data-testid="title-input"]', 'Write blog post');
        await page.fill('[data-testid="description-input"]', 'About test automation');
        await page.click('[data-testid="submit-button"]');

        await expect(page.locator('[data-testid="task-card"]').first())
            .toContainText('Write blog post');
    });

    test('user can delete a task', async ({ page }) => {
        await page.goto('/tasks');
        const initialCount = await page.locator('[data-testid="task-card"]').count();

        page.on('dialog', (dialog) => dialog.accept());
        await page.locator('[data-testid="task-card"]').first()
            .locator('[data-testid="delete-button"]').click();

        await expect(page.locator('[data-testid="task-card"]'))
            .toHaveCount(initialCount - 1);
    });
});

Playwright E2Eテスト実行結果

Claude Code は React コンポーネントのソースを読み取り、data-testid を使ったセレクタを正確に生成しています。

ダイアログ形式のUIにも対応した操作手順まで含まれていました。

E2E テストを導入すると、テストの自動化だけでなく、実行画面を「証跡」として残せるという副次的なメリットもあります。

5.1 スクリーンショットをエビデンスとして残せる

Playwright の利点のひとつが、実行時のスクリーンショットを自動で保存し、テストのエビデンスとして残せることです。撮影タイミングは設定で切り替えられます。

  • screenshot: 'only-on-failure' … テストが失敗したときだけ自動撮影する
  • screenshot: 'on' … 毎回自動撮影する
  • await page.screenshot({ path: 'evidence/create-task.png' }) … 任意のタイミングで撮影する

撮影した画像は 'test-results/' 配下に出力され、HTMLレポート('playwright show-report')からまとめて確認できます。
手動テストで行っていた証跡スクリーンショットの取得をコードに組み込めるため、不具合報告やレビュー時の説明材料としても活用できます。

下は、テスト実行中に Playwright が撮影した画面です。
テストがフォームに自動入力した内容('【E2Eテスト】…' というテストデータ)がそのまま写っており、どの操作時点のスクリーンショットなのかが一目で分かります。

テスト実行中にPlaywrightが撮影した画面(エビデンス)。テストが自動入力したデータが写っている


■ まとめ

Claude Code は、ソースコードを読み取ったうえで、Unit・Feature・E2E の3層すべてを実装に即して生成し、実行・修正まで一貫して任せられます。

テストの作成コストを大きく下げられるため、これまで手が回らなかったテスト整備にもぜひ活用してみてください。

前の記事