PestはPHPUnitの上に構築された、テストをよりシンプルに書くためのフレームワークです。そのPestが公式に推奨しているLaravelのためのプラグインpest-plugin-laravelを使って既存のテストを書き直してみました。Pestが掲げる「読みやすく理解しやすい」テストコードに、少し近づけるでしょうか。

Pest導入、基本の記述方法については以下をご参照ください。
PHPUnitテストをpest-plugin-driftでPestへ変換
Pestでモック・例外のテスト

pest-plugin-laravelをインストール・テストファイル作成

Pestはすでにインストールしてあると仮定して、以下のコマンドでプラグインをインストールします。

$ composer require pestphp/pest-plugin-laravel --dev

このプラグインをインストールしたことで、まず、新しいartisanコマンドpest:testが使えるようになっています。
ファイル名をPestTestと指定して以下のコマンドを実行します。

$ php artisan pest:test PestTest

tests/Feature/PestTest.phpにテストファイルが作成されます。

it('has pest page', function () {
    $response = $this->get('/pest');

    $response->assertStatus(200);
});

デフォルトでは、tests/Featureのディレクトリにファイルが作成されますが、--unitオプションをつけるとtest/Unitにファイルが作成されます。

もちろん、Laravel標準のmake:testコマンドで作成しても問題ありません。

$ php artisan make:test PestTest

pest-plugin-laravelを使った記述方法

プラグインインストールでテストの記述も少し簡単になります。Laravelのテストでよく使うテストヘルパーを$this->を使わず呼び出せるようになります。例えば以下のようなユーザーログアウトのテストの場合

test('users can logout', function () {

    $this->actingAs($this->user)
        ->post('/logout')
        ->assertRedirect('/');

    $this->assertGuest();
});

プラグインの機能を使って書き直すと、こんな風になります。

use function Pest\Laravel\{actingAs, assertGuest}; // インポートする

test('users can logout', function () {

    actingAs($this->user)
        ->post('/logout')
        ->assertRedirect('/');

    assertGuest();
});

actingAsassertGuestを直接呼び出すことができるようになるため、$this->の記述がなくなってコードが少しすっきりしました。名前空間関数のインポートが必要ですので、useをお忘れなく。

他にも、よく使われるHTTPリクエストの場合

プラグイン導入前

...
    $response = $this->get('/login');
...
    $response = $this->post('/login', [
        'email'    => 'test@example.com',
        'password' => 'password',
    ]);
...

導入後

use function Pest\Laravel\{get, post};

...
    $response = get('/login');
...
    $response = post('/login', [
        'email'    => 'test@example.com',
        'password' => 'password',
    ]);
...

データベースのアサーション

プラグイン導入前

...
    $this->assertDatabaseHas(User::class, ['email' => 'test@example.com']);
...

導入後

use function Pest\Laravel\assertDatabaseHas;
...
    assertDatabaseHas(User::class, ['email' => 'test@example.com']);
...

他にも色々なコマンドが対応していますので、詳しくはgithubをご覧ください。

プラグインの機能を使って、以前にご紹介したパスワードリセットのテストをPest形式に書き換えたものが以下になります。(書き換え前のPHPUnitのテストコードはリンクからご確認ください)

use function Pest\Laravel\{post, assertGuest, assertAuthenticated};

test('password can be reset with valid token', function () {
    Notification::fake();

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

    post('/forgot-password', ['email' => $user->email]);

    Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) {
        $response = post('/reset-password', [
            'token' => $notification->token,
            'email' => $user->email,
            'password' => 'new_password',
            'password_confirmation' => 'new_password',
        ]);

        $response->assertRedirect('login')
            ->assertSessionHas('status', 'パスワードをリセットしました。');

        return true;
    });

    assertGuest();

    post('/login', [
        'email' => $user->email,
        'password' => 'new_password',
    ]);

    assertAuthenticated();
});

postassertGuestassertAuthenticatedの箇所が、$this->を省略した形に置き換えられています。「すごく変わった!」という訳ではないものの、やはり元のテストよりは細かい部分が見やすくなっています。テストコードが増えてくるとかなり違いがあるのではないでしょうか。

データセット

もう1つ、このプラグインが提供しているコマンドpest:datasetをご紹介します。

$ php artisan pest:dataset データセット名

このコマンドはtests/Datasets/にデータセット用のファイルを作成してくれます。データセットの機能自体は元からPestにあるものですが、artisanコマンドを使ってより便利に作成できるようになりました。

例として、以下のテストからデータセットを別ファイルに切り出してみます。ログインに失敗するテストと、そのデータセットをwith()で渡しています。

test('users cannot authenticate with invalid credentials', function (string $email, string $password) {
    post('/login', [
        'email'    => $email,
        'password' => $password,
    ])->assertStatus(302);

    assertGuest();
})->with([
    '間違ったパスワード'    => ['test@example.com', 'wrong-password'],
    '間違ったメールアドレス' => ['wrong@example.com', 'password'],
]);

まずはデータセットのファイルを作成します。ファイル名はLoginDataとて、以下のコマンドを実行します。

$ php artisan pest:dataset LoginData

これでtests/Datasetsにファイルが作成されましたので、テストからデータセットの部分をこちらに移行します。

dataset('invalidLoginData', [
    '間違ったパスワード'    => ['test@example.com', 'wrong-password'],
    '間違ったメールアドレス' => ['wrong@example.com', 'password'],
]);

データセット名はinvalidLoginDataとしました。

元のテストコードの方は、with()の引数にデータセット名であるinvalidLoginDataを渡してあげればOKです。

test('users cannot authenticate with invalid credentials', function (string $email, string $password) {
・・・
})->with('invalidLoginData');

まとめ

このプラグインを使っても劇的にテストコードが短くなる訳ではなく、「少し良くなる」というものなので、すでにテストの書き方が確立されている環境にわざわざインストールする必要はなさそうです。が、Laravelの開発コアメンバーが開発しているプラグインなので、新しいプロジェクトでPestのテストを書いてゆくという場合には導入してみるのも面白そうだなと感じました。

メルマガ購読の申し込みはこちらから。

By hmatsu