【Laravel】テストコードの説明

Laravel

この記事のゴール

  • 前回書いた記事で使ったテストコードの理解

 ※前回書いた記事はこちら

前回書いたテストコード

簡単なテストケースを書いただけのつもりでしたが、だいぶ長くなったので

細切れで説明していきます。

テストクラスの命名ルール

class 宣言でクラス名の命名ルールですが、基本的には機能単位のテストになるので、

テスト対象の機能名に「Test」を接尾辞として付けたものを名称にします。

ここではテスト対象の機能が「TODO」機能なので「Todo」を機能名とし、

その後ろに「Test」を付けて「TodoTest」としています。

このクラス名とファイル名は必ず合わせましょう。

違っているとテスト時に叱られます。

「TodoTest」クラスなので、ファイル名は「TodoTest.php」です。

ファイル名は、作成時にartisanコマンドを使っていれば気にならないかもしれません。

テストメソッドの命名ルール

厳密な規則としては最初の「testで始める」ことと、PHPの関数名のルールに従うことだけです。

開発現場によって命名規則が変わると思います。

一応、筆者が採用しているルールは次の通りです。

  • test で始める
  • 単語間の空白はアンダースコア「_」で埋める
  • 次にメソッド名を入れる
  • 次にテスト内容がわかる説明的な名前を入れる

テスト実行時にエラーが出た場合、このメソッド名が説明的だと対応しやすくなります。

説明:冒頭部分

まずは最初の部分を見ていきましょう。

<?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
{

3行目で名前空間の定義があります。Laravelは機能テストを重視しているので、

Tests\Feature となっています。

5行目~7行目の use宣言3行分はデフォルトです。

8行目の use宣言は、Todo モデルを使うということです。

10行目の class 宣言でクラス名の命名ルールに則り「TodoTest」クラスにしています。

extends TestCase でLaravelの機能を使えるようにしたTestCaseクラスを継承します。

説明:TODOリスト表示のテストメソッド

これは TodoControllerのindex()メソッドのテストをするメソッドと言えると思います。

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);
        }
    }

メソッド名は「test_index_can_get_todo_list」、

「indexがTODOリストを取得できる」ことを確認するテストです。

        Todo::truncate();

truncate()で一旦、todosテーブルの中身を空にしています。(厳密にはテーブル作りなおし)

        $todos = Todo::factory()->count(5)->create();

ファクトリーを使って5行分、ダミーデータのレコード挿入をしています。

その結果作成された5行分のレコードの内容を、EloquentのCollectionオブジェクトとして

$todos に格納しています。後で検証に使います。

        $response = $this->get(route('todo.index'))
                         ->assertSee('TODOリスト')
                         ->assertSee('新規登録')
                         ->assertOk();

route(‘todo.index’) でTODOリストページのURLを取得しています。

http://localhost/todo/ ですね。

$this->get() の引数にURLを渡すとそこにアクセスした結果をオブジェクトで返すので、

メソッドチェーンで次々に検証を続けることができます。

assertSee(‘TODOリスト’) で、HTTPレスポンのHTML内に「TODOリスト」があるか

検証しています。あればOK、なければエラーになります。

assertOk() はHTTPレスポンスコードが200であることを検証しています。

assertStatus(200) と同じです。

Laravel で利用できるこれらのアサーションは公式サイトで確認しましょう。

Laravel - The PHP Framework For Web Artisans
Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing ...
        foreach ($todos as $todo) {
            $response->assertSee($todo->todo);
        }

最初に作成した5行分のレコードについて、

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();
        }
    }

ここも最初に truncate() でテーブルを空にしています。

それからダミーデータを5行分挿入し、対象レコードのデータを$todosに格納しています。

5行分のレコード全てについて、詳細ページにアクセスし、

該当データが表示されていることを検証しています。

            $this->get(route('todo.detail', ['id' => $todo->id]))

詳細ページのURLにはID指定が必用なので、$todo->id で渡しています。

説明:TODO新規登録フォーム表示のテスト

    public function test_create_can_be_rendered(): void
    {
        $this->get(route('todo.create'))
             ->assertSee('TODO新規登録')
             ->assertSee('やること:')
             ->assertSee('登録')
             ->assertSee('取消')
             ->assertOk();
    }

特に説明は要らないと思います。表示の確認とHTTPステータスコードの確認です。

説明: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']);
    }

これはページはありませんが、重要な機能として存在するのでテストします。

        $todo = Todo::factory()->definition();

覚えていますか?ファクトリー作成時にダミーデータを配列で返すメソッドを

作ったと思いますが、そのメソッド名が definition() です。

※覚えていない場合はこちらで確認

そのメソッド呼び出しで、登録するためのデータを配列として受け取っています。

        $this->post(route('todo.store'), $todo)
             ->assertValid()
             ->assertRedirect(route('todo.index'));

post() メソッドは第一引数がURL、第二引数がPOSTするデータのハッシュ配列です。

コントローラー側でPOSTデータのバリデーションが行われるので、

assertValid() でバリデーションエラーが無いことを検証しています。

assertRedirect() で、引数のURLにリダイレクトのレスポンスが返ったことを検証しています。

注意が必要なのは、ここで受け取っているHTTPレスポンスが、

リダイレクト先のものではなく、アクセスしたURLが返したリダイレクト指定の内容

であることです。HTTPレスポンスコードが301または302で、

レスポンスヘッダとHTML内にリダイレクト先のURLが表示されています。

        $this->get(route('todo.index'))
             ->assertSee($todo['todo']);

というわけで、リダイレクト先のURLには改めてアクセスする必要があります。

説明:TODO編集フォーム表示のテスト

    public function test_edit_can_be_rendered(): void
    {
        Todo::truncate();
        $todos = Todo::factory()->count(5)->create();
        foreach ($todos as $todo) {
            $this->get(route('todo.edit', ['id' => $todo->id]))
                 ->assertSee('TODO編集')
                 ->assertSee('取消')
                 ->assertSee('やること:')
                 ->assertSee($todo->todo)
                 ->assertSee('状態:')
                 ->assertSee('未完')
                 ->assertSee('完了')
                 ->assertSee('登録')
                 ->assertOk();
       }
    }

もう見ただけでわかると思います。

テーブルを空にしてからダミーデータ5行挿入、

5行分それぞれの編集フォームにアクセスして

該当データが表示されているかを確認しています。

説明:TODOデータ更新のテスト

    public function test_update_can_update(): void
    {
        Todo::truncate();
        $todos = Todo::factory()->count(5)->create(['done' => 0]);
        $id = 2;
        $todo = 'update:' . $todos[$id]->todo;
        $this->patch(route('todo.update', ['id' => $id]), [
                'todo' => $todo,
                'done' => 1,
             ])
             ->assertValid()
             ->assertRedirect(route('todo.detail', ['id' => $id]));
        $this->get(route('todo.index'))
             ->assertSee($todo);
    }

いつも通りテーブルを空にしてからダミーデータを5行挿入しています。

        $todos = Todo::factory()->count(5)->create(['done' => 0]);

create() の引数にハッシュ配列で項目の値を指定してレコード作成ができます。

ここでは、done = 0 つまり「未完」の状態のレコードを5行挿入しています。

        $todo = 'update:' . $todos[$id]->todo;

更新した内容であることが判別できるように、最初に「update:」を付加しています。

        $this->patch(route('todo.update', ['id' => $id]), [
                'todo' => $todo,
                'done' => 1,
             ])
             ->assertValid()
             ->assertRedirect(route('todo.detail', ['id' => $id]));

更新する際のHTTPメソッドはPATCHです。

更新する内容をハッシュで渡しています。先程の「update:」を付加したtodoと、

done=1 「完了」として渡しています。

assertValied() でバリデーションエラーが無いことの検証をしています。

assertRedirect() で詳細ページへのリダイレクトを検証しています。

        $this->get(route('todo.index'))
             ->assertSee($todo);

TODO一覧ページにアクセスして、更新したtodoが表示されいていることを検証しています。

説明:TODO削除のテスト

    public function test_destroy_can_destroy(): void
    {
        Todo::truncate();
        $todos = Todo::factory()->count(5)->create();
        $id = 2;
        $this->delete(route('todo.destroy', ['id' => $id]))
             ->assertRedirect(route('todo.index'));
        $todos = Todo::all();
        foreach ($todos as $todo) {
            $this->assertTrue($todo->id !== $id);
        }
    }

テーブルを空にしてからダミーデータを5行挿入しています。

id=2 のレコードを削除していきます。

        $this->delete(route('todo.destroy', ['id' => $id]))
             ->assertRedirect(route('todo.index'));

内容の削除時のHTTPメソッドは DELETE です。

TODO一覧ページへのリダイレクトを検証しています。

        $todos = Todo::all();
        foreach ($todos as $todo) {
            $this->assertTrue($todo->id !== $id);
        }

レコード全件取得して $todos に格納してから、

レコード毎に、id が 削除したレコードのものではないことを検証しています。

以上です。お疲れまでした。

コメント

タイトルとURLをコピーしました