🦆

Laravel 11.4 で導入された Exceptions facade を試す

2024/07/19に公開

まえがき

Laravel 11.4 で追加されたテスト用の Exceptions facade を試してみました。
今まで普通に使っていた expectException とは何が違うのか、軽く比較しながら見て行きたいと思います。

Laravel 日本語ドキュメント(例外処理)
ドキュメント:PHPUnit Expecting Exceptions

本題

Exceptions facade で追加されたテスト用メソッドは幾つかありますが、今回はもっとも基本の assertReported を使って見ます。なお、ここではあくまで HTTP テスト($this->get() とかするやつ)で見て行きます。

まずは、こんなコントローラ(web.php)があったとします。

Route::get('/', function () {
    throw new RuntimeException('例外が発生しましたよ');

    return view('welcome');
});

現実世界では、より具体的な例外クラスを投げたりしますが、その辺は今回は気にせず、上記に対するテストを今までの書き方で、PHPUnit で書いてみると、

use RuntimeException;

    public function test_例外が発生(): void
    {
        $this->withoutExceptionHandling();

        $this->expectException(RuntimeException::class);

        $this->get('/');
    }

こんな感じかと思います。HTTP テストなので、withoutExceptionHandling でエラーハンドリングを抑えつつ、PHPUnit の expectException を使って例外の発生テストを書いていました。

では、新しく追加された Exceptions facade を使うとどんな風に書けるかと言うと、

use Illuminate\Support\Facades\Exceptions;
use RuntimeException;

    public function test_assertReport(): void
    {
        Exceptions::fake();

        $this->get('/');

        Exceptions::assertReported(RuntimeException::class);
    }

おぉ!何となく良くなったような、そうでもないような…。少なくとも withoutExceptionHandling は不要になったものの、代わりに Exceptions::fake(); を書く必要がありますね。

しかしよくよく見てみますと、メソッド名は、assertThrows ではなく、assertReported となっています。実は $this->assertThrows というメソッドは Ver.9.12 からあったりする訳ですが、何故、assertReported よ?と思ったりします。という事で、試しに発生する例外クラスを RuntimeException ではなく、Laravel 内部の ModelNotFoundException とした場合、、、

web.php
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    throw new ModelNotFoundException('例外が発生しましたよ');

    return view('welcome');
});
テスト
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Exceptions;

    // 従来タイプ
    public function test_例外が発生(): void
    {
        $this->withoutExceptionHandling();

        $this->expectException(ModelNotFoundException::class);

        $this->get('/');
    }

    // 新タイプ
    public function test_assertReport(): void
    {
        Exceptions::fake();

        $this->get('/');

        Exceptions::assertReported(ModelNotFoundException::class);
    }

従来タイプは普通にテストにパスしますが、新タイプは、普通にテストに失敗します。
「何でやねん」と一瞬思ったりしますが、ModelNotFoundException というのは、レポート対象になってないんですね。レポート対象とは、分かり易く言ってしまえば、ログの対象か否かですね。

例えば、findOrFail() とか使って DB からモデルを取得しようとして見つからなくても、ログられない事からも ModelNotFoundException はレポート対象外という事は分かりますね。

ソース的には、この辺で分かったりします。

という事で、新タイプの Exceptions facade は、従来からの expectException と同じ目的で使えたりもしますが、ちょっと異なる所もあるぞ、という事が分かりました。

雑感

おかしな所等がありましたらコメント下さい。

Discussion