#28 Laravel基礎

ユニットテストとモック

ユニットテストとは

ユニットテスト? 個々のクラスやメソッドを独立して検証するテスト。外部依存を切り離してロジック単体が正しく動くかを確認する。 は、特定のクラスやメソッドを外部依存なしで単独テストする手法です。DBやメール送信など副作用のある処理は モック? テスト中に本物のクラスの代わりに使う偽物オブジェクト。メール送信やAPI呼び出しなど副作用のある処理を置き換えてテストできる。 (偽物オブジェクト)で置き換えます。


ユニットテストを作る

php artisan make:test PostServiceTest --unit

tests/Unit/PostServiceTest.php が生成されます。

<?php
// 📁 tests/Unit/PostServiceTest.php

namespace Tests\Unit;

use App\Services\PostService;
use App\Models\Post;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class PostServiceTest extends TestCase
{
    use RefreshDatabase;

    public function test_スラッグを正しく生成できる(): void
    {
        $service = new PostService();

        // タイトルからスラッグを生成するメソッドをテスト
        $slug = $service->generateSlug('Laravel入門 基礎編');

        $this->assertEquals('laravel-nyuumon-kiso-hen', $slug);
    }
}

Laravelのモック

Laravelには Mail, Notification, Storage, Queue などのFacadeをモック化するヘルパーが用意されています。

Mail のモック

// 📁 tests/Feature/RegisterTest.php

use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeMail;

public function test_登録するとウェルカムメールが送信される(): void
{
    // メールの実際の送信を止める
    Mail::fake();

    $response = $this->post('/register', [
        'name'                  => 'テストユーザー',
        'email'                 => 'test@example.com',
        'password'              => 'password123',
        'password_confirmation' => 'password123',
    ]);

    // WelcomeMail が送信されたことを確認
    Mail::assertSent(WelcomeMail::class);

    // 特定のアドレスに送られたか確認
    Mail::assertSent(WelcomeMail::class, function ($mail) {
        return $mail->hasTo('test@example.com');
    });
}

Notification のモック

use Illuminate\Support\Facades\Notification;
use App\Notifications\CommentPosted;

public function test_コメントを投稿すると通知が送信される(): void
{
    Notification::fake();

    $user = User::factory()->create();
    $post = Post::factory()->create(['user_id' => $user->id]);

    $this->actingAs(User::factory()->create())
         ->post("/posts/{$post->id}/comments", ['body' => 'テストコメント']);

    Notification::assertSentTo($user, CommentPosted::class);
}

Queue のモック

use Illuminate\Support\Facades\Queue;
use App\Jobs\SendDailyReport;

public function test_レポートジョブがキューに追加される(): void
{
    Queue::fake();

    $this->post('/admin/report/send');

    Queue::assertPushed(SendDailyReport::class);
}

Storage のモック

use Illuminate\Support\Facades\Storage;

public function test_サムネイルをアップロードして保存できる(): void
{
    Storage::fake('public');  // fakeディスクを使う

    $user = User::factory()->create();
    $file = \Illuminate\Http\UploadedFile::fake()->image('thumbnail.jpg', 800, 600);

    $response = $this->actingAs($user)->post('/posts', [
        'title'     => 'テスト投稿',
        'body'      => 'テスト本文',
        'thumbnail' => $file,
    ]);

    // ファイルが保存されたことを確認
    Storage::disk('public')->assertExists('thumbnails/' . $file->hashName());
}

Mockeryでクラスをモックする

外部APIクライアントなどを差し替える場合は Mockery を使います。

// 📁 tests/Feature/PaymentTest.php

use App\Services\PaymentGateway;
use Mockery;

public function test_支払いが完了すると注文が確定する(): void
{
    // PaymentGateway の偽物を作る
    $mockGateway = Mockery::mock(PaymentGateway::class);
    $mockGateway->shouldReceive('charge')
                ->once()
                ->with(5000)
                ->andReturn(['status' => 'success', 'transaction_id' => 'txn_123']);

    // サービスコンテナに偽物を登録
    $this->app->instance(PaymentGateway::class, $mockGateway);

    $user = User::factory()->create();
    $response = $this->actingAs($user)->post('/checkout', ['amount' => 5000]);

    $response->assertRedirect('/orders/complete');
    $this->assertDatabaseHas('orders', ['status' => 'paid']);
}

テストヘルパーとユーティリティ

// 時間を固定する
\Illuminate\Support\Carbon::setTestNow('2025-01-01 12:00:00');
// テスト終了後に元に戻す
\Illuminate\Support\Carbon::setTestNow();

// イベント発火を確認
\Illuminate\Support\Facades\Event::fake();
Event::assertDispatched(\App\Events\UserRegistered::class);

// HTTPクライアントのモック(外部APIのテスト)
\Illuminate\Support\Facades\Http::fake([
    'api.example.com/*' => Http::response(['status' => 'ok'], 200),
]);

まとめ

  • Mail::fake(), Notification::fake(), Queue::fake() でFacadeをモックできる
  • Storage::fake('disk') でファイル操作をテストできる
  • Mockery でサービスクラスを差し替えてテストできる
  • Carbon::setTestNow() で時刻を固定できる

次回はEloquentの便利なコレクションメソッドを学びます。