#27 Laravel基礎

フィーチャーテストを書く

テストとは

Laravelには2種類のテストがあります。

種類内容
フィーチャーテスト? HTTPリクエストを模倣してアプリ全体の動作を確認するテスト。ルートからDBまで実際に動かして結果を検証する。 HTTPリクエスト〜レスポンスまでアプリ全体を検証
ユニットテスト? 個々のクラスやメソッドを独立して検証するテスト。外部依存を切り離してロジック単体が正しく動くかを確認する。 個々のクラス・メソッドを単独で検証

この回は実務でよく書くフィーチャーテストから始めます。


テストの準備

データベース設定

テスト用に SQLite のインメモリDBを使うと速くて独立した環境が作れます。

# 📁 .env.testing(テスト専用の .env)

DB_CONNECTION=sqlite
DB_DATABASE=:memory:

または phpunit.xml に設定します。

<!-- 📁 phpunit.xml -->
<php>
    <env name="DB_CONNECTION" value="sqlite"/>
    <env name="DB_DATABASE" value=":memory:"/>
    <env name="MAIL_MAILER" value="array"/>
    <env name="QUEUE_CONNECTION" value="sync"/>
</php>

RefreshDatabase トレイト

テストクラスに RefreshDatabase を付けると、各テストの前後にDBをリセットしてくれます。

use Illuminate\Foundation\Testing\RefreshDatabase;

class PostTest extends TestCase
{
    use RefreshDatabase; // ← これを付ける
}

最初のフィーチャーテスト

php artisan make:test PostTest

tests/Feature/PostTest.php が生成されます。

<?php
// 📁 tests/Feature/PostTest.php

namespace Tests\Feature;

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

class PostTest extends TestCase
{
    use RefreshDatabase;

    // 投稿一覧が表示されるか
    public function test_投稿一覧を表示できる(): void
    {
        Post::factory()->published()->count(3)->create();

        $response = $this->get('/posts');

        $response->assertStatus(200);
        $response->assertViewHas('posts');
    }

    // 未ログインではフォームにアクセスできないか
    public function test_未ログインでは投稿フォームにアクセスできない(): void
    {
        $response = $this->get('/posts/create');

        $response->assertRedirect('/login');
    }

    // ログイン済みで投稿を作成できるか
    public function test_ログインユーザーは投稿を作成できる(): void
    {
        $user = User::factory()->create();

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

        $response->assertRedirect('/posts');
        $this->assertDatabaseHas('posts', [
            'title'   => 'テスト投稿',
            'user_id' => $user->id,
        ]);
    }
}

よく使うアサーション

// HTTPステータス
$response->assertStatus(200);
$response->assertOk();           // 200
$response->assertCreated();      // 201
$response->assertNotFound();     // 404
$response->assertForbidden();    // 403
$response->assertRedirect('/url');

// ビュー
$response->assertViewIs('posts.index');
$response->assertViewHas('posts');
$response->assertViewHas('posts', fn ($posts) => $posts->count() === 3);

// レスポンスの内容
$response->assertSee('テスト投稿');
$response->assertDontSee('削除済み');

// JSON
$response->assertJson(['status' => 'ok']);
$response->assertJsonPath('data.name', 'テスト');

// DB
$this->assertDatabaseHas('posts', ['title' => 'テスト']);
$this->assertDatabaseMissing('posts', ['title' => '削除済み']);
$this->assertDatabaseCount('posts', 3);

ログイン状態のテスト

$user = User::factory()->create();

// $user としてログインした状態でリクエストを送る
$this->actingAs($user)->get('/dashboard');

// 認証済みか確認
$this->assertAuthenticated();

// 未認証か確認
$this->assertGuest();

バリデーションエラーのテスト

public function test_タイトル未入力はバリデーションエラーになる(): void
{
    $user = User::factory()->create();

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

    $response->assertSessionHasErrors('title');
    $this->assertDatabaseCount('posts', 0);
}

テストを実行する

# 全テストを実行
php artisan test

# 特定のファイルのみ
php artisan test tests/Feature/PostTest.php

# テスト名でフィルター
php artisan test --filter="投稿を作成できる"

# 並列実行(高速)
php artisan test --parallel

まとめ

  • RefreshDatabase でテストごとにDBをリセットする
  • $this->get(), $this->post() でHTTPリクエストをシミュレートする
  • actingAs($user) でログイン状態を作れる
  • assertDatabaseHas() でDBへの書き込みを検証できる
  • assertSessionHasErrors() でバリデーションエラーを確認できる

次回はユニットテストとモックを使ったテストを学びます。