Documentation Index
Fetch the complete documentation index at: https://kawax.biz/llms.txt
Use this file to discover all available pages before exploring further.
なぜPestがLaravelのデフォルトになったか
Laravel 11より、laravel new でプロジェクトを作成する際に Pest がデフォルトのテストフレームワークとして選択されるようになりました。PHPUnit は長年 PHP の標準的なテストツールとして使われてきましたが、Pest はその PHPUnit をベースにしながら、より簡潔で読みやすいテスト記法を提供します。
PestはPHPUnit上で動作するため、既存のPHPUnitテストはそのまま実行できます。移行は段階的に進められます。
公式が Pest を採用した背景には、テストを書く際の摩擦を減らすという目標があります。クラスを定義してメソッドを書いてという儀式的な手順をなくし、「テストしたいこと」そのものに集中できる記法を提供することで、テストを書く習慣が自然と身につくようになります。
PHPUnitとの主な違い
テストの記法
最もわかりやすい違いはテストの書き方です。
test('ユーザーがログインできる', function () {
$user = User::factory()->create();
$response = $this->post('/login', [
'email' => $user->email,
'password' => 'password',
]);
$response->assertRedirect('/dashboard');
});
class AuthTest extends TestCase
{
public function test_user_can_login(): void
{
$user = User::factory()->create();
$response = $this->post('/login', [
'email' => $user->email,
'password' => 'password',
]);
$response->assertRedirect('/dashboard');
}
}
Pest の test() 関数はクロージャを受け取ります。クラスやメソッドの定義が不要なため、テストの意図が一行目から明確です。it() も同様に使えます。英語で書く場合は it('can login', ...) という自然な文として読めます。
expect() API
Pest の最大の特徴は expect() を使ったチェーン形式のアサーションです。
test('投稿一覧APIがJSON形式で返る', function () {
$posts = Post::factory(3)->create();
$response = $this->getJson('/api/posts');
expect($response->status())->toBe(200);
expect($response->json('data'))->toHaveCount(3);
expect($response->json('data.0.title'))->not->toBeEmpty();
});
expect($value)->toBe(), ->toBeNull(), ->toContain(), ->toHaveCount() など、英語として自然に読める形式でアサーションを書けます。複数のアサーションをメソッドチェーンでまとめることもできます。
expect($user)
->name->toBe('田中太郎')
->email->toBe('[email protected]')
->is_admin->toBeFalse();
セットアップとティアダウン
PHPUnit の setUp() に相当するのが beforeEach()、tearDown() に相当するのが afterEach() です。
beforeEach(function () {
$this->user = User::factory()->create();
$this->actingAs($this->user);
});
test('認証済みユーザーはプロフィールにアクセスできる', function () {
$response = $this->get('/profile');
$response->assertOk();
});
test('認証済みユーザーはプロフィールを更新できる', function () {
$response = $this->patch('/profile', ['name' => '新しい名前']);
$response->assertRedirect('/profile');
});
データセット — テーブルドリブンテスト
同じテストロジックを複数のデータで実行したい場合は dataset を使います。
test('無効なメールアドレスはバリデーションエラーになる', function (string $email) {
$response = $this->postJson('/api/users', [
'name' => '田中太郎',
'email' => $email,
]);
$response->assertUnprocessable();
})->with([
'メールなし' => [''],
'形式不正' => ['notanemail'],
'二重@' => ['a@@example.com'],
]);
各データセットのラベル('メールなし' など)がテスト名に付加されるため、どのパターンで失敗したかが一目でわかります。
arch() テスト — アーキテクチャの自動チェック
Pest の arch() はコードベースの構造を検証するテストです。「コントローラーがモデルに直接依存していないか」「モデルが Eloquent を継承しているか」といったアーキテクチャルールを自動で検証できます。
test('モデルはEloquentを継承している', function () {
arch()->expect('App\Models')
->toExtend(Illuminate\Database\Eloquent\Model::class);
});
test('コントローラーはfinalで定義されている', function () {
arch()->expect('App\Http\Controllers')
->toBeFinal();
});
test('サービスクラスはコントローラーに依存しない', function () {
arch()->expect('App\Services')
->not->toUse('App\Http\Controllers');
});
arch() テストはソースコードを静的解析して実行されます。実際にHTTPリクエストを送ったりデータベースにアクセスしたりしないため、非常に高速です。
Pest にはよく使われるアーキテクチャルールのプリセットも用意されています。
test('Laravelのアーキテクチャルールに従っている', function () {
arch()->preset()->laravel();
});
laravel() プリセットはモデルの命名・コントローラーの継承・ミドルウェアの構造など、Laravelの一般的な慣習に沿ったルールをまとめて検証します。
Laravelプロジェクトでの実践例
テストの作成
php artisan make:test UserTest
Laravel 11 以降でデフォルト設定のまま実行すると、Pest 形式のテストファイルが生成されます。
<?php
test('example', function () {
expect(true)->toBeTrue();
});
Laravelのテストヘルパーをそのまま使う
Pest は Laravel の TestCase を継承しているため、actingAs()、assertDatabaseHas()、HTTP テストヘルパーなど、すべての Laravel テストヘルパーがそのまま使えます。
use App\Models\Post;
use App\Models\User;
test('ユーザーは自分の投稿を削除できる', function () {
$user = User::factory()->create();
$post = Post::factory()->for($user)->create();
$response = $this
->actingAs($user)
->delete("/posts/{$post->id}");
$response->assertRedirect('/posts');
$this->assertModelMissing($post);
});
test('他ユーザーの投稿は削除できない', function () {
$user = User::factory()->create();
$otherUser = User::factory()->create();
$post = Post::factory()->for($otherUser)->create();
$this
->actingAs($user)
->delete("/posts/{$post->id}")
->assertForbidden();
$this->assertModelExists($post);
});
ファクトリーとデータベーストランザクション
RefreshDatabase や DatabaseTransactions トレイトも uses() で簡単に適用できます。ファイル先頭に書けばそのファイル全体に適用されます。
uses(Tests\TestCase::class, Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature');
tests/Pest.php に一度設定しておくと、すべての Feature テストで RefreshDatabase が自動的に有効になります。個別のテストファイルで上書きすることも可能です。
既存PHPUnitテストとの共存方法
Pest と PHPUnit は同じプロジェクト内で共存できます。既存の PHPUnit テストを書き換える必要はなく、新しいテストから Pest 形式で書き始めればよいだけです。
./vendor/bin/pest # Pestで全テストを実行(PHPUnitテストも含む)
./vendor/bin/pest --filter="ユーザー" # テスト名でフィルタリング
php artisan test # ArtisanコマンドでもPestが動く
php artisan test は Laravel 11 以降、Pest がインストールされている場合は自動的に Pest を使って実行します。
移行の進め方
- まず
tests/Pest.php に uses() の設定を追加する
- 新しく追加するテストは Pest 形式で書く
- 既存の PHPUnit テストは動作を確認しながら少しずつ移行する
急いで全テストを書き換える必要はありません。Pest のシンタックスは PHPUnit と比べて学習コストが低いため、チームへの浸透も比較的スムーズです。
まとめ
| 比較項目 | PHPUnit | Pest |
|---|
| テスト定義 | クラス + メソッド | test() / it() 関数 |
| アサーション | $this->assert*() | expect()->to*() |
| セットアップ | setUp() | beforeEach() |
| データ駆動 | @dataProvider | ->with() |
| アーキテクチャ検証 | なし | arch() |
| 実行速度 | 標準 | 同等(PHPUnit上で動作) |
Pest は PHPUnit の置き換えではなく、PHPUnit をラップした上位レイヤーです。Laravel との統合も深く、スターターキット生成時からデフォルトで選択されるほどになっています。既存プロジェクトへの導入コストは低く、新規のテストから試し始めるだけで恩恵を受けられます。
テストを書く量が増えれば、バグの発見が早くなり、リファクタリングへの心理的ハードルも下がります。Pest はその「テストを書くこと自体の摩擦」を減らすための道具です。
Pest公式ドキュメント
データセット、カバレッジ、並列実行など、Pestの全機能については公式ドキュメントを参照してください。