Laravel テスト用メソッドの assertRedirect を地味に拡張してみる
前置き
Laravel Ver. 8.75 で、テスト用メソッドの assertRedirect() も改善され、ステータスコードを調べる assertXxxx 系は、$this->withoutExceptionHandling(); を呼び出さなくてもエラー(例外)を自動で出力してくれるようになりました(出揃いました)。
ただ、assertRedirect() は、まだ未完成な部分があるので、無理矢理対応させて見る事にしました。(ほぼ自己満足の世界か…)
参考:[8.x] Display exception message with assertRedirect #39841
参考:Laravel 8.51 で、テスト時の withoutExceptionHandling() の呼び出しが、ほぼほぼ不要になった
問題点(改善点)
assertRedirect() は、第1引数に URL を指定して、その URL にリダイレクトされるかをテストする事ができます。その URL にリダイレクトされない場合、例えば、以下のように出力されます。(要点部分のみ)
--- Expected
+++ Actual
@@ @@
-'http://localhost/contact/confirm'
+'http://localhost'
これだと何を間違ったのか、具体的には分かりません。
少なくとも私の場合は、99% バリデーションエラーが発生していて、元の URL に転送されようとしているので、期待するリダイレクト先の URL と一致しなかったりします。
dumpSession() を挟んで、以下みたいにやれば、原因が分かったりするのですが、それが地味に面倒なのですね。
$this->post(...)->dumpSession()->assertRedirect(...);
そこで、下で紹介するメソッドで元のメソッドをオーバーライドすれば、以下の感じで問題箇所を指摘してくれます。(要点部分のみ)
Expected URL: http://localhost/contact/confirm
Actual URL : http://localhost
Expected response status code [201, 301, 302, 303, 307, 308] but received 302.
The following errors occurred during the request:
会社名は、必須です。
メールアドレスが一致しません。
これで問題箇所が一目瞭然です。
英語部分の「Expected response status code [201, 301, 302, 303, 307, 308] but received 302.」は、完全におかしなメッセージになってたりもしますが、そこは気にしない事にしておきます。
また、例によって問題行の指摘は無くなり、拡張部分の指摘になったりもしますが、そこも目をつむります…。
php artisan test
98▕ // 確認画面へ
➜ 99▕ $this->post(~~~, $valid)->assertRedirect(~~~);
ではなく、こんな感じ
89▕ if ($expectedUri !== $actualUri) {
➜ 90▕ PHPUnit::fail($returnMessage);
以下、拡張用のメソッドです。
以前の記事の tests/MyTestResponse.php に以下のメソッドを追加して下さい。
public function assertRedirect($uri = null)
{
$detailMessage = $this->statusMessageWithDetails('201, 301, 302, 303, 307, 308', $actual = $this->getStatusCode());
if ($actual === 500) {
PHPUnit::fail($detailMessage);
}
PHPUnit::assertTrue(
$this->isRedirect(), 'Response status code ['.$this->getStatusCode().'] is not a redirect status code.'
);
if (is_null($uri)) {
return $this;
}
$expectedUri = app('url')->to($uri);
$actualUri = app('url')->to($this->headers->get('Location'));
$returnMessage = <<<EOT
Expected URL: $expectedUri
Actual URL : $actualUri
$detailMessage
EOT;
if ($expectedUri !== $actualUri) {
PHPUnit::fail($returnMessage);
}
return $this;
}
雑感
これで少し楽できます。
問題箇所等ありましたらコメント下さい。
Discussion