この記事のゴール
- テストエラーが発生したときの対応の仕方がわかる
前提条件
- Laravelでの自動テストコードを書いて実行したことがある
※Laravelでの自動テストについては前回の記事をどうぞ。
これからやること
- テストコードの準備
- テスト実行
- エラーの確認と原因の特定
- コードの修正
- テストの再実施
- コードの再修正
- テストの再々実施
テストコードの準備
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use App\Models\Todo;
class TodoTest extends TestCase
{
public function testExample(): void
{
}
public function test_index_can_get_todo_list(): void
{
Todo::truncate();
$response = $this->get(route('todo'))
->assertSee('TUDUリスト')
->assertSee('新規登録')
->assertOk();
foreach ($todos as $todo) {
$response->assertSee($todo->todo);
}
}
public function test_retrieve_can_be_rendered(): void
{
Todo::truncate();
$todos = Todo::factory()->count(5)->create();
foreach ($todos as $todo) {
$this->get(route('todo.detail'))
->assertSee('TODO詳細')
->assertSee('[編集]')
->assertSee('[削除]')
->assertSee('todo一覧')
->assertSee('やること: ' . $todo->todo)
->assertSee('状態:' . (1 == $todo->done ? '官僚' : ''))
->assertOk();
}
}
public function test_store_can_store(): void
{
$todo = Todo::factory()->definition();
$todo['done'] = null;
$todo['id'] = 6;
$this->post(route('todo.store'), $todo)
->assertValid()
->assertRedirect(route('todo.index'))
->assertSee($todo['todo']);
}
}
テスト実行
では、このコードを実行してみます。
php artisan test
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror01-1024x217.png)
イイ感じで始まりました。
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror02.png)
あれっ?「3 failed, 1 riskey, 24 passed (57 assertions)」って表示されました。
失敗3、危険1、合格24ですね。
エラーの確認と原因の特定
こういうときには、表示をスクロールして遡り、最初のエラーや警告から見ていきます。
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror03-1024x321.png)
クラス名「Tests\Feature\TodoTest」の左側に「FAILED」と表示され、その下に
各テストメソッドの結果が表示され、失敗した理由とコードのエラー発生箇所が表示されます。
では、最初の「example」メソッドの黄色い警告ですが、
「This test did not perform any assertions」とあります。
「このテストは何の検証も実施してないじゃないか」ということです。
public function testExample(): void
{
}
中身が空のメソッドですね。最低1つはアサーションを記述しないと叱られます。
次に、「index can get todo list」のエラーで「Route[todo] not defined.」とあります。
「Route情報の[todo]なんて定義されてないよ!」ということです。
$response = $this->get(route('todo'))
あ、ここはTODO一覧ページのURLなので、
route(‘todo.index’) にしないといけませんね。
次の内容を確認していきます。
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror04-1024x311.png)
「retrieve can be rendered」の箇所で、次のメッセージが出ています。
Missing required parameter for [Route: todo.detail] [URI: todo/{id}] [Missing parameter:id].
Route: todo.detail で要求される引数が見当たりません。
[URIの定義は: todo/{id}] です。
[引数の id が見当たりません。] ということです。
$this->get(route('todo.detail'))
あ、route() の引数で id 渡すの忘れてましたね。
次を見ていきます。
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror05-1024x295.png)
「store can store」の箇所で、「Response has unexpected validation errors:」
とあります。バリデーションエラーが次のように表示されています。
{
"done": [
"The done field is required."
]
}
項目 done は必須だよと言っています。テストコードを見てみます。
$todo['done'] = null;
あ、何やってるんですかね。
ここで確認できる警告とエラーは以上です。原因も全て特定しました。
全てテストコード側のミスでしたね。
コードの修正1:testExample()
では、テストコードを修正していきます。
testExample() の警告「This test did not perform any assertions」の対応です。
public function testExample(): void
{
}
これは不要なメソッドでした。メソッドを丸ごと削除します。
コードの修正2:test_index_can_get_todo_list()
「Route[todo] not defined.」の対応をしていきます。
$response = $this->get(route('todo'))
route(‘todo’) を route(‘todo.index’) に修正します。
$response = $this->get(route('todo.index'))
一応、正しいルーティングになっているかどうかを、
[routes/web.php] で確認すると確実です。
コードの修正3:test_retrieve_can_be_rendered()
Missing required parameter for [Route: todo.detail] [URI: todo/{id}] [Missing parameter: id].
この対応をしていきます。
$this->get(route('todo.detail'))
route() の第二引数に id を指定する必要があります。
$this->get(route('todo.detail', ['id' => $todo->id]))
確かに id を指定しました。
コードの修正4:test_store_can_store()
「Response has unexpected validation errors:」の対応です。
項目 done の必須チェックでエラーでしたね。
$todo['done'] = null;
この行は不要なので削除です。
これで修正は完了です。
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use App\Models\Todo;
class TodoTest extends TestCase
{
public function test_index_can_get_todo_list(): void
{
Todo::truncate();
$response = $this->get(route('todo.index'))
->assertSee('TUDUリスト')
->assertSee('新規登録')
->assertOk();
foreach ($todos as $todo) {
$response->assertSee($todo->todo);
}
}
public function test_retrieve_can_be_rendered(): void
{
Todo::truncate();
$todos = Todo::factory()->count(5)->create();
foreach ($todos as $todo) {
$this->get(route('todo.detail', ['id' => $todo->id]))
->assertSee('TODO詳細')
->assertSee('[編集]')
->assertSee('[削除]')
->assertSee('todo一覧')
->assertSee('やること: ' . $todo->todo)
->assertSee('状態:' . (1 == $todo->done ? '官僚' : ''))
->assertOk();
}
}
public function test_store_can_store(): void
{
$todo = Todo::factory()->definition();
$todo['id'] = 6;
$this->post(route('todo.store'), $todo)
->assertValid()
->assertRedirect(route('todo.index'))
->assertSee($todo['todo']);
}
}
テスト再実行
修正完了したのでテストを再実行してみましょう。
手早く確認したいので、TodoTest.php だけテストしてみましょう。
php artisan test tests/Feature/TodoTest.php
この場合は、最後の引数に、artisan からの相対パスでファイル指定します。
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror06-1024x384.png)
あれ、またエラーです。
「To contain: TUDUリスト」って、正しくは「TODOリスト」ですよね。
このように、一つ修正して通っても、その次でエラーになることがあります。
こうして、一つ一つ確実にエラーを潰していきます。
エラーを潰す度にコードの品質が上がると思えば楽しいものです。と思ってください。
次の確認にいきましょう。
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror07-1024x320.png)
「To contain: 状態:官僚」って、「状態:完了」ですよね。
次の確認です。
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror08.png)
これ、ちょっとよくわからないですよね。
バリデーション通っているんだから、一覧に表示されるはずでしょ?
こういうときには、レスポンスのHTMLを出力してみましょう。
public function test_store_can_store(): void
{
$todo = Todo::factory()->definition();
$todo['id'] = 6;
$this->post(route('todo.store'), $todo)
->assertValid()
->assertRedirect(route('todo.index'))
->dump()
->assertSee($todo['todo']);
}
assertRedirect() の下に dump() を入れてみました。この状態でテスト実行してみます。
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror09.png)
POST後のHTTPレスポンスのHTMLが表示されました。
リダイレクトの指定が書かれているだけでした。
ブラウザがHTTPレスポンスのステータスコード301または302を受け取り
HTTPレスポンスヘッダに含まれるリダイレクト先のURLを見てから、
ブラウザが自らリダイレクト先のURLへアクセスするというのが
リダイレクトの仕組みです。
なので、リダイレクト先のコンテンツを取得するには、
新たにアクセスしなければなりません。
コードの再修正:test_index_can_get_todo_list()
原因の特定をしたので、再びコードを修正していきましょう。
->assertSee('TUDUリスト')
TUDU → TODO に修正します。
->assertSee('TODOリスト')
コードの再修正:test_test_retrieve_can_be_rendered()
->assertSee('状態:' . (1 == $todo->done ? '官僚' : ''))
官僚 → 完了 に修正します。
->assertSee('状態:' . (1 == $todo->done ? '完了' : ''))
コードの再修正:test_store_an_store()
public function test_store_can_store(): void
{
$todo = Todo::factory()->definition();
$todo['id'] = 6;
$this->post(route('todo.store'), $todo)
->assertValid()
->assertRedirect(route('todo.index'));
->assertSee($todo['todo']);
}
$todo[‘id’] = 6; とかも要らないですよね。削ります。
あと、リダイレクト後にTODOリストページに改めてアクセスします。
public function test_store_can_store(): void
{
$todo = Todo::factory()->definition();
$this->post(route('todo.store'), $todo)
->assertValid()
->assertRedirect(route('todo.index'));
$this->get(route('todo.index'))
->assertSee($todo['todo']);
}
よし、全部直した!
テスト再々実行
今度こそ通りますように。そう、システム開発では神様にお願いしたくなるので、
開発神社というものがあるのですよ。
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror10.png)
あれれ、また何かでました。今度はテンプレート側ですかね。
<body>
<h1>{{$pageTitle}}</h1>
<a href="{{route('todo.create')}}">新 規 登 録</a><br />
@isset($todos)
@foreach($todos as $todo)
あらら、余計なスペースが入って「新 規 登 録」になってますね。
修正してテスト再々々実行です。
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror11.png)
うわー、またエラー。「Undefined variable $todos」→「未定義の変数 $todos」。
public function test_index_can_get_todo_list(): void
{
Todo::truncate();
$response = $this->get(route('todo.index'))
->assertSee('TODOリスト')
->assertSee('新規登録')
->assertOk();
foreach ($todos as $todo) {
$response->assertSee($todo->todo);
}
}
あ、「Todo::truncate();」で空にしたまま、レコード作成していなかったのと、
作成したレコードを $todo に格納する箇所がないですね。修正します。
public function test_index_can_get_todo_list(): void
{
Todo::truncate();
$todos = Todo::factory()->count(5)->create();
$response = $this->get(route('todo.index'))
->assertSee('TODOリスト')
->assertSee('新規登録')
->assertOk();
foreach ($todos as $todo) {
$response->assertSee($todo->todo);
}
}
テストを再々々々実行します。
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror12.png)
今度こそオールグリーン!でもこれで安心してはいけません。
念のため、全てのテストを実行しましょう。
php artisan test
![](https://macocci7.net/blog/wp-content/uploads/2023/06/testerror13.png)
これが本当のオールグリーン!これで安心して眠れますね。
おやすみなさい。
コメント