Laravel テスト 2026:Pest 5、TIA、モックと技術面接質問

Pest 5、Test Impact Analysis、Mockery、ファサードフェイク、アーキテクチャテストを用いた Laravel テストのベストプラクティスを解説。ユニットテスト、フィーチャーテスト、モック戦略、Laravel 開発者向けの面接質問を網羅します。

LaravelテストとPestによるモック・アーキテクチャテスト・面接対策2026ガイド

Laravel テストのベストプラクティスは、Pest 5、TIA(Test Impact Analysis)エンジン、PHPUnit 13 の登場により大きく変化しました。Laravel 13 は Pest をファーストクラスでサポートしており、生の PHPUnit よりも表現力豊かで簡潔なテスト体験を提供します。本記事では、プロダクション環境のアプリケーションと技術面接の両方で重要となるパターンを解説します。

Pest 5 が標準に

Pest 5(PHPUnit 13 上に構築)は Laravel 13 のデフォルトテストフレームワークです。PHP 8.4 以降が必要で、TIA エンジン、ファーストパーティの AI エージェント検証、PHPStan 統合、時間バランスシャーディングが標準で組み込まれています。

Laravel 13 プロジェクトにおける Pest 5 のセットアップ

新規の Laravel 13 アプリケーションでは Pest が自動的にスキャフォールドされます。既存プロジェクトでは、PHPUnit 13 との互換性が主な考慮事項となるため、Pest 4 からの移行は数分で完了します。設定は tests/Pest.php に集約され、グローバルトレイトとヘルパーメソッドがここに登録されます。

tests/Pest.phpphp
use Illuminate\Foundation\Testing\RefreshDatabase;

pest()
    ->extend(Tests\TestCase::class)
    ->use(RefreshDatabase::class)
    ->in('Feature');

この単一ファイルが、従来の CreatesApplication トレイトとベーステストクラスの継承を置き換えます。RefreshDatabase トレイトは各テストをデータベーストランザクションでラップし、変更を自動的にロールバックします。

tests/Feature/UserRegistrationTest.phpphp
use App\Models\User;

it('registers a new user with valid data', function () {
    $response = $this->postJson('/api/register', [
        'name' => 'Jane Doe',
        'email' => 'jane@example.com',
        'password' => 'SecurePass123!',
        'password_confirmation' => 'SecurePass123!',
    ]);

    $response->assertStatus(201)
        ->assertJsonStructure(['user' => ['id', 'name', 'email']]);

    expect(User::where('email', 'jane@example.com')->exists())->toBeTrue();
});

Pest の expect() API は PHPUnit のアサーションと自然に連携します。assertJsonStructure でレスポンス構造を検証し、expect()->toBeTrue() でデータベース状態を確認します。両方のスタイルは競合なく共存できます。

Test Impact Analysis:Pest 5 の注目機能

TIA エンジンは、大規模コードベースにおいて Pest 5 を革新的なものにしています。初回実行時に、Pest はどのテストがどのファイルに影響するかを記録します。以降の実行では、変更の影響を受けるテストのみが実行され、それ以外はキャッシュされた結果が再利用されます。

bash
# 初回実行:ベースラインを記録(PCOV または Xdebug が必要)
php artisan test

# 以降の実行:影響を受けるテストのみ実行
php artisan test

Laravel Cloud のテストスイートは、19,000 以上のテストを含みますが、TIA 有効化により約 3 分から 5 秒に短縮されました。このエンジンは PHP ファイルだけでなく、マイグレーション、Blade テンプレート、共有 JavaScript コンポーネントの変更も検出します。

TIA はベースラインを記録するためにカバレッジドライバー(PCOV または Xdebug)を必要とします。CI パイプラインでは、キャッシュされたマッピングが実行間で永続化されるため、パフォーマンス向上効果は累積的になります。

Laravel におけるユニットテストとフィーチャーテストの違い

Laravel におけるユニットテストとフィーチャーテストの区別は、フレームワークのどの部分が起動されるかを決定します。ユニットテストはアプリケーションコンテナなしで実行されるため高速ですが、純粋なロジックに限定されます。フィーチャーテストはアプリケーション全体を起動し、HTTP 呼び出し、データベースクエリ、サービス解決を可能にします。

tests/Unit/PriceCalculatorTest.phpphp
use App\Services\PriceCalculator;

describe('PriceCalculator', function () {
    it('applies a percentage discount correctly', function () {
        $calculator = new PriceCalculator();

        // 150.00 の基本価格から 20% オフ
        $result = $calculator->applyDiscount(150.00, 20);

        expect($result)->toBe(120.00);
    });

    it('rejects negative discount values', function () {
        $calculator = new PriceCalculator();

        expect(fn () => $calculator->applyDiscount(100.00, -5))
            ->toThrow(InvalidArgumentException::class);
    });
});

ユニットテストは外部依存のない独立したクラスを対象とします。describe ブロックは関連するアサーションをグループ化し、Pest 5 は複雑なテスト階層のためのネストされた describe ブロックをサポートします。

フィーチャーテストは Laravel テストスイートの大部分を占めるべきです。ルートミドルウェアの誤り、バリデーションルールの欠落、Eloquent リレーションの不具合など、ユニットテストでは検出できない統合バグを捕捉します。一般的なルール:コードがデータベース、HTTP レイヤー、またはファサードに触れる場合、フィーチャーテストを書くべきです。

ファサードと Mockery によるモック戦略

Laravel テストにおけるモックは、テスト対象コードを外部依存から分離します。Laravel ファサードはキュー、イベント、通知、メール、ストレージ用の組み込みフェイク実装を提供します。Mockery はそれ以外すべてを処理します。

tests/Feature/OrderProcessingTest.phpphp
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Queue;
use App\Mail\OrderConfirmation;
use App\Jobs\ProcessPayment;

it('dispatches payment job and sends confirmation email', function () {
    // Mail と Queue の両ファサードをフェイク化
    Mail::fake();
    Queue::fake();

    $user = User::factory()->create();
    $order = Order::factory()->for($user)->create([
        'total' => 99.99,
        'status' => 'pending',
    ]);

    // Act: HTTP 経由で注文を確定
    $this->actingAs($user)
        ->postJson("/api/orders/{$order->id}/confirm")
        ->assertOk();

    // Assert: 支払いジョブが正しい金額でディスパッチされた
    Queue::assertPushed(ProcessPayment::class, function ($job) use ($order) {
        return $job->order->id === $order->id
            && $job->order->total === 99.99;
    });

    // Assert: 確認メールがユーザーに送信された
    Mail::assertSent(OrderConfirmation::class, function ($mail) use ($user) {
        return $mail->hasTo($user->email);
    });
});

ファサードフェイクはフレームワークレベルで呼び出しをインターセプトし、実際のメール送信やジョブのディスパッチを防ぎます。クロージャベースのアサーションで、各コンポーネントに渡された正確なデータを検証します。

境界でモックする

内部ドメインクラスのモックは避けてください。境界でモックする:サードパーティ API、メール、キュー、ファイルシステム。過度なモックはテストを脆弱にし、実装の詳細に密結合させます。

ファサードではない依存関係については、コンストラクタで注入し、Mockery を使用します:

tests/Feature/PaymentGatewayTest.phpphp
use App\Services\PaymentGateway;
use App\Services\StripeClient;

it('charges the customer through the payment gateway', function () {
    // Stripe クライアントのモックを作成
    $stripeClient = Mockery::mock(StripeClient::class);
    $stripeClient->shouldReceive('charge')
        ->once()
        ->with('cus_abc123', 5000, 'usd')
        ->andReturn(['status' => 'succeeded', 'id' => 'ch_xyz']);

    // コンテナにモックをバインド
    $this->app->instance(StripeClient::class, $stripeClient);

    $gateway = app(PaymentGateway::class);
    $result = $gateway->processCharge('cus_abc123', 50.00);

    expect($result['status'])->toBe('succeeded');
});

$this->app->instance() でモックをバインドすると、テスト期間中は実際の実装が置き換えられます。Mockery は charge が期待される引数で正確に 1 回呼び出されたことを検証します。

Pest プリセットによるアーキテクチャテスト

Pest 5 には、コードベース全体で構造ルールを強制するアーキテクチャテストが含まれています。これらのテストは実行時ではなく AST に対して実行されるため、非常に高速です。

tests/Architecture/ArchitectureTest.phpphp
arch('controllers do not use Eloquent directly')
    ->expect('App\Http\Controllers')
    ->not->toUse('Illuminate\Database\Eloquent');

arch('services are final classes')
    ->expect('App\Services')
    ->toBeFinal();

arch('no debugging functions in production code')
    ->expect(['dd', 'dump', 'var_dump', 'ray'])
    ->not->toBeUsed();

最初のルールはコントローラーがデータベースを直接クエリすることを禁止し、サービスレイヤーパターンを強制します。2 番目はサービスが拡張できないようにし、継承の複雑さを軽減します。3 番目は本番環境に到達する前にデバッグステートメントの残存を検出します。

Laravel 固有のプリセットも利用可能です:

tests/Architecture/LaravelPresetTest.phpphp
arch()->preset()->laravel();
arch()->preset()->security();
arch()->preset()->php();

この 3 行で数十のルールを強制します:モデルが正しいベースクラスを継承しているか、コントローラーにビジネスロジックが含まれていないか、eval() や md5() のような安全でない関数がハッシュに使用されていないか、標準的な PHP 規約が遵守されているかを検証します。

Laravelの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

ミューテーションテストによるテスト品質の検証

コードカバレッジはテスト中にどの行が実行されたかを測定します。ミューテーションテストはさらに進んで:ソースコードを変更し、テストがその変更を検出するかをチェックします。ミューテーションが生き残った場合、テストスイートにギャップがあります。

bash
# 特定のクラスに対してミューテーションテストを実行
php artisan test --mutate --class=App\\Services\\PriceCalculator

Pest 5 は > を >= に変更、return ステートメントの削除、ブール条件の反転などのミューテーションを導入します。ミューテーションスコアが 80% 未満の場合、通常はテストがエッジケースをチェックせずに出力を検証していることを示します。

php
// 例:高カバレッジだが低ミューテーションスコアのテスト
it('calculates shipping cost', function () {
    $cost = calculateShipping(weight: 5.0, zone: 'domestic');

    // 結果が数値であることのみをチェック
    expect($cost)->toBeFloat();
});

// 改善版:正確な値をアサートしてミューテーションを捕捉
it('calculates domestic shipping for 5kg package', function () {
    $cost = calculateShipping(weight: 5.0, zone: 'domestic');

    // 正確なアサーションが演算子と値のミューテーションを捕捉
    expect($cost)->toBe(12.50);
});

最初のテストは計算ロジックが完全に誤っていても、float を返す限りパスします。2 番目のテストは数式のミューテーションが発生すると即座に失敗します。具体的なアサーションはより高いミューテーションスコアを生み出します。

データベーステストパターンとファクトリー

Laravel モデルファクトリーは、手動の配列構築なしに現実的なテストデータを生成します。Pest 5 とファクトリーを組み合わせることで、可読性が高くメンテナンスしやすいデータセットアップが実現します。

tests/Feature/ArticlePublishingTest.phpphp
use App\Models\Article;
use App\Models\User;

it('publishes a draft article and updates the timestamp', function () {
    $author = User::factory()->create(['role' => 'editor']);

    $article = Article::factory()
        ->for($author, 'author')
        ->draft()
        ->create(['title' => 'Testing Best Practices']);

    $this->actingAs($author)
        ->patchJson("/api/articles/{$article->id}/publish")
        ->assertOk()
        ->assertJsonPath('data.status', 'published');

    $article->refresh();

    expect($article->status)->toBe('published')
        ->and($article->published_at)->not->toBeNull()
        ->and($article->published_at->isToday())->toBeTrue();
});

draft() ファクトリーステートは未公開記事のデフォルト値を設定します。expect() アサーションを ->and() でチェーンすることで自然言語のように読め、説明的なメッセージで失敗します。

パラレルテストとシャーディング

php artisan test --parallel を実行すると、テストが複数のプロセスで実行されます。Laravel は各プロセスに個別のテストデータベースを自動的に作成し、データの競合を防ぎます。Pest 5 の時間バランスシャーディングは、カウントではなく実際の実行時間でテストを分散し、すべての CI シャードが同時に終了するようにします。

Laravel テストの技術面接でよくある質問

Laravel ポジションの技術面接では、テストの知識が頻繁に問われます。最もよく出題される質問と、的確な回答のポイントを紹介します。

Laravel テストにおける fake()、mock()、spy() の違いは何ですか?

ファサードの fake() はファサード全体をインメモリ実装に置き換えます(Mail::fake、Queue::fake)。Mockery 経由の mock() は実行前に期待を設定し、それが満たされないと失敗します。spy() は事前の期待を設定せずにインタラクションを記録し、実行後のアサーションを可能にします。選択は、テストが動作を検証する必要があるか(mock)、インタラクションを記録するか(spy)、または副作用を防ぐか(fake)によって異なります。

RefreshDatabase と DatabaseTransactions の違いは何ですか?

RefreshDatabase はマイグレーションを一度実行し、各テストをトランザクションでラップします。DatabaseTransactions はデータベースに既に正しいスキーマがあることを前提とし、テストをトランザクションでラップするだけです。RefreshDatabase はデータベースがまだ存在しない可能性がある CI パイプラインではより安全です。DatabaseTransactions はスキーマが最新であることが保証されている場合はより高速です。

フィーチャーテストとユニットテストのどちらを優先すべきですか?

フィーチャーテストは HTTP レイヤー、データベース、または Laravel サービスと相互作用するあらゆるコードをカバーすべきです。ユニットテストはフレームワーク依存のない純粋な関数と値オブジェクト用に予約されます。典型的な Laravel アプリケーションでは、フィーチャーテストがユニットテストを約 3:1 以上の比率で上回ります。これは、ほとんどの Laravel コードが本質的にフレームワークと統合されているという現実を反映しています。

Test Impact Analysis は CI パフォーマンスをどのように改善しますか?

TIA は初回実行時にどのテストがどのソースファイルに触れるかを記録します。以降の実行では、Pest は変更されたファイルの影響を受けるテストのみを実行し、変更されていないテストにはキャッシュされた結果を再利用します。数分かかっていた大規模スイートが数秒で完了できます。トレードオフ:TIA は依存関係マップを構築するためにカバレッジドライバー(PCOV または Xdebug)を必要とし、初回実行は通常の実行よりも時間がかかります。

ミューテーションテストはコードカバレッジを超えてテスト品質をどのように向上させますか?

コードカバレッジは実行パスを測定します。テストは意味のあるアサーションなしにすべてのメソッドを呼び出すことで 100% カバレッジを達成できます。ミューテーションテストはソースコードを変更(演算子の変更、return の削除、ブールの反転)し、少なくとも 1 つのテストが失敗することを検証します。生き残ったミューテーションは、アサーションが緩すぎるか、完全に欠けていることを明らかにします。php artisan test --mutate を実行すると、標準のカバレッジと共にミューテーションスコアのパーセンテージが生成されます。

より多くの Laravel 面接質問については、SharpSkill の質問バンクが認証、サービスコンテナパターン、Eloquent リレーション、キューアーキテクチャをカバーしています。

HTTP レスポンスと JSON アサーションのテスト

Laravel の HTTP テストヘルパーは、ステータスコード、ヘッダー、JSON 構造、リダイレクト先を検証します。Pest と組み合わせることで、簡潔な統合テストを形成します。

tests/Feature/ApiAuthenticationTest.phpphp
use App\Models\User;

describe('API Authentication', function () {
    it('rejects unauthenticated requests with 401', function () {
        $this->getJson('/api/profile')
            ->assertUnauthorized();
    });

    it('returns the authenticated user profile', function () {
        $user = User::factory()->create([
            'name' => 'John Doe',
            'email' => 'john@example.com',
        ]);

        $this->actingAs($user)
            ->getJson('/api/profile')
            ->assertOk()
            ->assertJson([
                'data' => [
                    'name' => 'John Doe',
                    'email' => 'john@example.com',
                ],
            ]);
    });

    it('validates required fields on registration', function () {
        $this->postJson('/api/register', [])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['name', 'email', 'password']);
    });
});

describe ブロックは認証関連のテストをグループ化します。各テスト名は実装ではなく期待される動作を記述します。assertJsonValidationErrors メソッドは、特定のフィールドにバリデーションエラーメッセージがあることを確認します。

Sources

Pest 5 による Laravel テストで覚えておくべきこと

  • Pest 5 と Laravel 13 は、流暢な設定 API、expect() チェーン、自動テスト検出によりボイラープレートを排除します
  • TIA エンジンは影響を受けたテストのみを再実行し、カバレッジの精度を犠牲にすることなく、大規模スイートの実行を数分から数秒に短縮します
  • フィーチャーテストは Laravel テストスイートのコアを形成し、ユニットテストは分離されたビジネスロジック用に予約すべきです
  • ファサードフェイク(Mail::fake()、Queue::fake())はフレームワークレベルのモックを処理し、Mockery はコンテナ経由で注入されたサードパーティ依存関係を処理します
  • アーキテクチャテストは構造ルール(コントローラーで Eloquent 禁止、デバッグ関数禁止)を実行時オーバーヘッドなしで強制します
  • --mutate によるミューテーションテストは、コードカバレッジだけでは見逃す弱いアサーションを検出します
  • ファクトリーステートと expect()->and() アサーションのチェーンにより、テストデータのセットアップが可読性を保ち、アサーションが具体的になります
  • Laravel 面接準備において、mock/fake/spy、RefreshDatabase の動作、TIA、ミューテーションテストの理解は、基本的なカバレッジを超えたテスト成熟度を示します

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

今日のチャレンジ

Laravel のバグを見つけられますか

実際のコード、隠れたバグ、1日1回。アカウントなしで試せます。

Anthony Fillion-Maillet

執筆

Anthony Fillion-Maillet

SharpSkill 創業者

10 年以上フルスタック開発に携わっています。SharpSkill を運営し、ここで公開される内容に責任を負っています。

2026年9月2日 更新

タグ

#laravel
#testing
#pest
#php
#mocking
#best-practices
#interview

共有

関連記事