Laravel、Feature テスト時、ドカンと出るエラーの表示順を入れ替えて、少し楽をする
本題
【追記 2022-02-14】
~~~ここから~~~
Laravel Ver.9.1で、一番知りたいエラーメッセージの表示が下部にも表示されるようになります。詳しくは、こちらの記事をご覧下さい。ですので、下記の記事は不要となりますが、参考までに残しておきます。Ver.9.1以降は、下記は適用させないで下さい。新機能がうまく機能しなくなります。
~~~ここまで~~~
以前記事にしましたが、Ver.8.51以降は、Featureテストで、assertOk() などでステータスコードをチェックしていれば、withoutExceptionHandling() を書かなくても、例外エラーがドカンと出てくれるようになりました。便利な世の中になりました。
ただ、これはすごく便利なのですが、私の中である事に気づきました。
「エラーの根本原因は一番上に書いてあるので、毎回毎回、マウスのホイールを6~7回回して、画面を上に移動しなければならない…」
怠け者の私には、これが微妙に苦痛でした。そこで、Stack trace の上下が入れ替わるものを書いてみました。以下がそれです。
tests/TestCase.php
<?php
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Log\Events\MessageLogged;
use Illuminate\Testing\LoggedExceptionCollection;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
protected function setUp(): void
{
parent::setUp();
// エラーログを上下逆順にして出力
$this->app->make('events')->listen(MessageLogged::class, function ($event) {
if (! isset($event->context['exception'])) {
return;
}
tap($this->app->make(LoggedExceptionCollection::class), function ($collection) {
$collection->push(
implode("\n", array_reverse(explode("\n", $collection->last())))
);
});
});
}
}
これを付ける前と後でどのように出力が変わるか、記載しておきます。
(__PATH__とある箇所は、実際のパスが入ります)
Before
• Tests\Feature\Http\Controllers\StationControllerTest > ○○○○のテスト
Expected response status code [200] but received 500.
The following exception occurred during the request:
Error: Call to undefined function App\Http\Controllers\hoge() in __PATH__/app/Http/Controllers/StationController.php:31
Stack trace:
#0 __PATH__/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\Http\Controllers\StationController->list()
#1 __PATH__/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\Routing\Controller->callAction()
#2 __PATH__/vendor/laravel/framework/src/Illuminate/Routing/Route.php(262): Illuminate\Routing\ControllerDispatcher->dispatch()
#3 __PATH__/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\Routing\Route->runController()
#4 __PATH__/vendor/laravel/framework/src/Illuminate/Routing/Router.php(695): Illuminate\Routing\Route->run()
#5 __PATH__/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\Routing\Router->Illuminate\Routing\{closure}()
(~~~ 略 ~~~)
#50 __PATH__/vendor/phpunit/phpunit/src/Framework/TestSuite.php(678): PHPUnit\Framework\TestCase->run()
#51 __PATH__/vendor/phpunit/phpunit/src/Framework/TestSuite.php(678): PHPUnit\Framework\TestSuite->run()
#52 __PATH__/vendor/phpunit/phpunit/src/Framework/TestSuite.php(678): PHPUnit\Framework\TestSuite->run()
#53 __PATH__/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(670): PHPUnit\Framework\TestSuite->run()
#54 __PATH__/vendor/phpunit/phpunit/src/TextUI/Command.php(143): PHPUnit\TextUI\TestRunner->run()
#55 __PATH__/vendor/phpunit/phpunit/src/TextUI/Command.php(96): PHPUnit\TextUI\Command->run()
#56 __PATH__/vendor/phpunit/phpunit/phpunit(92): PHPUnit\TextUI\Command::main()
#57 {main}
Failed asserting that 200 is identical to 500.
at tests/Feature/Http/Controllers/StationControllerTest.php:30
26▕ {
27▕ Station::factory(20)->create();
28▕
29▕ $this->get('xxx/yyy')
➜ 30▕ ->assertOk();
31▕ }
32▕
33▕ /** @test */
34▕ function △△△のテスト()
After
• Tests\Feature\Http\Controllers\StationControllerTest > ○○○○のテスト
Expected response status code [200] but received 500.
The following exception occurred during the request:
#57 {main}
#56 __PATH__/vendor/phpunit/phpunit/phpunit(92): PHPUnit\TextUI\Command::main()
#55 __PATH__/vendor/phpunit/phpunit/src/TextUI/Command.php(96): PHPUnit\TextUI\Command->run()
#54 __PATH__/vendor/phpunit/phpunit/src/TextUI/Command.php(143): PHPUnit\TextUI\TestRunner->run()
#53 __PATH__/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(670): PHPUnit\Framework\TestSuite->run()
#52 __PATH__/vendor/phpunit/phpunit/src/Framework/TestSuite.php(678): PHPUnit\Framework\TestSuite->run()
#51 __PATH__/vendor/phpunit/phpunit/src/Framework/TestSuite.php(678): PHPUnit\Framework\TestSuite->run()
#50 __PATH__/vendor/phpunit/phpunit/src/Framework/TestSuite.php(678): PHPUnit\Framework\TestCase->run()
(~~~ 略 ~~~)
#5 __PATH__/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\Routing\Router->Illuminate\Routing\{closure}()
#4 __PATH__/vendor/laravel/framework/src/Illuminate/Routing/Router.php(695): Illuminate\Routing\Route->run()
#3 __PATH__/vendor/laravel/framework/src/Illuminate/Routing/Route.php(205): Illuminate\Routing\Route->runController()
#2 __PATH__/vendor/laravel/framework/src/Illuminate/Routing/Route.php(262): Illuminate\Routing\ControllerDispatcher->dispatch()
#1 __PATH__/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\Routing\Controller->callAction()
#0 __PATH__/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\Http\Controllers\StationController->list()
Stack trace:
Error: Call to undefined function App\Http\Controllers\hoge() in __PATH__/app/Http/Controllers/StationController.php:31
Failed asserting that 200 is identical to 500.
at tests/Feature/Http/Controllers/StationControllerTest.php:30
26▕ {
27▕ Station::factory(20)->create();
28▕
29▕ $this->get('xxx/yyy')
➜ 30▕ ->assertOk();
31▕ }
32▕
33▕ /** @test */
34▕ function △△△のテスト()
出力後は、一番下にいて、「Error: Call to undefined function App\Http\Controllers\hoge()」の箇所が、一番知りたい箇所なのですが、Beforeだと一番上の辺りにあり、移動が大変です。
一方、Afterの場合、すぐ下にあるので、移動せずとも一目瞭然です。これで開発も更に楽しめそうです。
ちなみに、After でも「Expected response status code [200] but received 500.」の箇所は、上にあるままです。Laravel本体を直してしまえば、これを下に持ってくるのも簡単なのですが、上記の方法だと、ちょっと無理ですね。
当初は、プルリクも検討したのですが、他にもエラー内容吐き出す箇所はあったりして、でもそっちは順番入れ替えると微妙だったり、などなど考えると、異論反論も多そうだなと思ったりして、ひとまず自分用に作成したという感じです。(ややハック的なやり方ですが)
ちなみに
ステータスコードを調べるアサーションは幾つかありますが、現在、assertRedirect() のみは、他と少しコードが異なる為、このエラー内容出力機能に対応してなく、従来通り、withoutExceptionHandling() を呼び出さないといけません。
それを直すべく、プルリクを送った方がいましたが、見事却下されています。
[8.x] Add exception message to assertRedirect #39236
ただ、このプルリク程修正をしなくても、もっと簡易(6~7行レベル)の修正で対応可能そうなので、また一段落したら、プルリク検討してみます。(まぁ、却下されるかも知れませんが)
雑感
できたてほやほやですので、もし不具合など見つけたら、コメント下さい。
Discussion