【Laravel】テストコード入門!テストの書き方を解説

Laravel

こんにちは!

今回はLaravelでの「テストコードの書き方」というテーマで解説していきます。

Webアプリケーション開発において、コードの品質を向上させて安心して開発を進めるためには、適切なテストが不可欠です。Laravelのテストコードの書き方を学んで、堅牢なアプリケーションを構築していきましょう!

本記事では、以下のようなことがわかります。

  1. テストコードを書くための設定方法
  2. テストコードの書き方
  3. テストデータを用意する方法
読者
読者

「Laravelでテストコードを書く方法を知りたい」

とお考えの方は参考になるかと思います。

それでは、さっそくみていきましょう!

テストコードを書くためのセットアップ

まずはLaravelをインストールします。

$ composer create-project laravel/laravel --prefer-dist test

インストールが完了したら、テスト用の.envファイルを作成していきます。

$ cp .env.example .env.testing

.env.testingを開いて、DBの環境変数を設定していきます。

DB_CONNECTION=testing
DB_HOST=ホスト名
DB_PORT=ポート番号
DB_DATABASE=データベース名
DB_USERNAME=root
DB_PASSWORD=root
DB_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock

データベース名やポート番号などは環境に合わせて適時設定してください。筆者はMAMP環境で開発しているため、「DB_SOCKET」も追記しています。

次に、テスト用のDBを作成します。

MySQLにログインして以下のコマンドを実行します。

mysql> CREATE DATABASE test_db;

DBが作成されたことを確認しておきます。

mysql> SHOW DATABASES;

「config/database.php」ファイルを開いて、LaravelのDB設定を行います。

connections配列の中に以下を追記します。

'testing' => [
    'driver' => 'mysql',
    'url' => env('DATABASE_URL'),
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'prefix_indexes' => true,
    'strict' => true,
    'engine' => null,
    'options' => extension_loaded('pdo_mysql') ? array_filter([
        PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
    ]) : [],
],

connections配列はDBの接続設定を行っています。

次に、phpunitの設定を行います。

「phpunit.xml」ファイルを開いて、以下の部分を編集します。

<env name="DB_CONNECTION" value="testing"/>
<env name="DB_DATABASE" value="test_db"/>

これでひとまず初期セットアップ完了です!

テストデータの準備

テストデータは基本的にSeederとFactoryを用いて作成していきます。

今回はユーザーデータと投稿データを作成していこうと思います。そのために、まずはマイグレーションファイルを作成してpostsテーブルを用意していきます。

$ php artisan make:migration create_posts_table

マイグレーションファイルには以下のように記述しておきます。

/**
 * Run the migrations.
 */
public function up(): void
{
  Schema::create('posts', function (Blueprint $table) {
      $table->id();
      $table->unsignedBigInteger('user_id')->comment('ユーザID');
      $table->string('title')->comment('タイトル');
      $table->text('body')->comment('投稿本文')->nullable();
      $table->text('image_path')->comment('画像パス')->nullable();
      $table->timestamps();

      $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
  });
}

/**
 * Reverse the migrations.
 */
public function down(): void
{
  Schema::dropIfExists('posts');
}

次に、Factoryを作成していきましょう。ユーザーのFactoryクラスはデフォルトで用意されているため、投稿データのみです。

$ php artisan make:factory PostFactory

definitionメソッドに以下のように書いておきます。

public function definition(): array
{
    $user_id = User::where('email', 'admin@example.com')->first()->id;
    return [
        'user_id' => $user_id,
        'title' => fake()->word(),
        'body' => fake()->text()
    ];
}

ユーザーデータは「admin@example.com」というメールアドレスで作成する予定なので、このテストユーザーを指定してuser_idに入れるようにしています。

続いてSeederを用意していきます。

ユーザーデータは一人分しか用意するつもりはないので、UserSeederは以下のようになります。

use App\Models\User;
use Illuminate\Support\Facades\Hash;

public function run(): void
{
    $user = new User();
    $user->name = 'admin';
    $user->email = 'admin@example.com';
    $user->password = Hash::make('adminpass');
    $user->save();
}

一人分であれば、直接SQLを実行しても良いかなとは思います。

投稿用のSeederを作成するため、以下のコマンドを実行します。

$ php artisan make:seeder PostSeeder

投稿用のテストデータは10件分用意します。

public function run(): void
{
    \App\Models\Post::factory(10)->create();
}

最後に、「DatabaseSeeder」に登録しておきます。

public function run(): void
{
    $this->call(UserSeeder::class);
    $this->call(PostSeeder::class);
}

このDatabaseSeederに各Seederクラスを登録しておかないと、Seederが走りません。初心者の方は引っ掛かりがちなので覚えておきましょう。

こちらのSeederは後ほどテストコードを書く際に必要になります。

これでテストデータの準備は完了です!

Laravelでテストデータを作る際は、このようにSeederとFactoryを活用しながら作成していくのが基本です。

テストコードの書き方

お待たせしました。本題のテストコードの書き方について解説していきます。

Laravelのテストコードはtestsフォルダの中に格納されます。

testsフォルダの中に「Feature/」と「Unit/」がありますが、それぞれ機能テストと単体テストのディレクトリになります。

メソッド単位やクラス単位でテストしたい場合はUnitディレクトリの方にテストコードを書き、一つの機能全体をテストしたい場合はFeatureディレクトリでテストコードを書くことになります。

今回はFeatureディレクトリの方に書いていきます。

$ php artisan make:test Post/StoreTest

今回は仮で投稿機能のテストコードを書いてみたいと思います。

まずはテスト対象のコードを書いてみます。

「routes/web.php」を編集します。

Route::post('/post/store', [PostController::class, 'store'])->name('post.store');

次に、コントローラを作成します。

$ php artisan make:controller PostController

Postモデルも作成します。

$ php artisan make:model Post

それでは、PostControllerで投稿処理を書いていきます。

public function store(Request $request)
{
    $request->validate([
        'title' => ['required', 'max:255'],
        'body' => ['required'],
    ]);

    $post = new Post();
    $post->title = $request->title;
    $post->body = $request->body;
    $post->user_id = $request->user()->id;
    $post->save();

    return redirect()->route('post.index');
}

こちらのテストコードを書いていきます。

それでは、テストファイルに戻って以下のように書きます。

<?php

namespace Tests\Feature\Post;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\UploadedFile;
use Tests\TestCase;
use App\Models\User;

class StoreTest extends TestCase
{
    use RefreshDatabase;

    private $login_user;

    public function setUp(): void
    {
        parent::setUp();
        $this->seed();
        $this->login_user = User::where('email', 'admin@example.com')->first();
        $this->actingAs($this->login_user)->post(route('login'), [
            'email' => 'admin@example.com',
            'password' => 'adminpass',
        ]);
    }
    /**
     * A basic feature test example.
     */
    public function test_store(): void
    {
        // login_userが認証を通過しているとする
        $this->assertAuthenticatedAs($this->login_user);
        $response = $this->actingAs($this->login_user)->post(route('post.store'), [
            'title' => 'Test Title',
            'body' => 'Test Body',
        ]);
        $response->assertStatus(302);
        $response->assertRedirect(route('post.index')); // リダイレクト確認
        $this->assertDatabaseHas('posts', [
            'title' => 'Test Title',
            'body' => 'Test Body',
        ]); // DBに登録されていることを確認
    }
}

setUp()メソッドはテストコード実行前に走ります。このメソッドの中でテストデータの準備などを行うことができます。また、いくつテストケースがあっても毎回走ってくれるので、共通で用意しておきたいテストデータなどがある場合は、このメソッド内で用意するのが基本です。

テストコードではassert系のメソッドで処理が正しく行われているか確認できます。

以下、本テストコードで使われているassert系メソッドです。

  • assertStatus・・・レスポンスのステータスコードを確認する
  • assertRedirect・・・指定したルートへのリダイレクトを確認する
  • assertDatabaseHas・・・指定したテーブル内に指定したデータが登録されている確認する

これら以外にもたくさんあるので、ぜひ調べてみてください。

また、以下のRefreshDatabaseトレイトを使うことで一つのテストが終わると自動的にDBをクリーンアップしてくれます。

use RefreshDatabase;

これを行わないと、2つ目以降のテストを実行する際に前のテストのデータがDBに残ってしまい、他のテストに影響が出てしまいます。

動作確認

最後に、動作確認を行います。

テストコードを実行する前に、以下を必ず実行してください。

$ php artisan config:clear && php artisan cache:clear

これを行わないと、テスト用ではない方の本番のDBに変更が入ってしまう可能性があります。

それでは、テストコードを実行してみます。

$ php artisan test

以下のようになれば成功です。

逆に、失敗する場合は以下のようになります。

失敗した場合はエラーメッセージが出力されるので、エラーメッセージを確認し適時対処しましょう。

それでは、今回は以上です。

コメント

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