Laravelの標準機能を使ってユニットテストしてみる【初心者向け】

Featured image of the post

概要

Laravelでは標準でPHPUnitが導入されており、簡単にユニットテストができる✅

実際にユニットテストをするまでの流れを簡単に解説する。

フォルダの構造

最初からPHPUnitに関するファイルが用意されているので解説する✅

よく編集するファイル

プロジェクト
    |
    |----tests
    |      |
    |      |----Feature(👈PHPUnitの実行フォルダ)
    |      |       |
    |      |       |----ExampleTest.php
    |      |
    |      |----Unit   (👈PHPUnitの実行フォルダ)
    |      |       |
    |      |       |----ExampleTest.php
    |
    |----phpunit.xml   (👈PHPUnitの設定ファイル)


💡
FeatureとUnitはほぼ同じ!
FeatureはDB操作ができるが少し重くなる。

テストを実行してみる

サンプルのテストが用意されているのでいきなりテストを試せる✅

以下のコマンドを実行する。

vendor/bin/phpunit

すると以下のようにテストが成功する。

Image in a image block

デフォルトのテスト内容

具体的には以下の2ファイルがテストされていた✅

tests/Feature/ExampleTest.php

class ExampleTest extends TestCase
{
    public function test_example()
    {
        $response = $this->get('/');

				// トップページのステータスコードが200か?
        $response->assertStatus(200);
    }
}

tests/Unit/ExampleTest.php

class ExampleTest extends TestCase
{
    public function test_example()
    {
				// 引数がtrueになっているか?
        $this->assertTrue(true);
    }
}

💡
ごく簡単なテストが最初から用意されている。

テストを作ってみる

テスト用のデータベースを設定

テストで本番のデータベースを使うと本番環境が汚染されてしまう。

そのためテスト用のデータデータを設定しておく✅

💡
方法を3つ紹介するので好きな方法で設定すればOK!

【方法1】一時的なデータベースを使う

PHPUnitのテストのときだけメモリ上のデータベースを使う設定。

簡易なテストのときにおすすめ!

phpunit.xml

	<php>
        <server name="APP_ENV" value="testing"/>
        <server name="BCRYPT_ROUNDS" value="4"/>
        <server name="CACHE_DRIVER" value="array"/>

				👇このコメントアウトを解除する!
        <!-- <server name="DB_CONNECTION" value="sqlite"/> -->
        <!-- <server name="DB_DATABASE" value=":memory:"/> -->

        <server name="MAIL_MAILER" value="array"/>
        <server name="QUEUE_CONNECTION" value="sync"/>
        <server name="SESSION_DRIVER" value="array"/>
        <server name="TELESCOPE_ENABLED" value="false"/>
    </php>

💡
これだけで一時的なデータベースが使われるようになる。

【方法2】テスト用データベースを使う

テスト環境用のデータベースを設定する方法。

PHPUnit以外でもテストしたいときにおすすめ!

✅まず「database」フォルダに「database_test.sqlite」を新規作成する。

✅その後「.env」ファイルをコピーして「.env_本番環境」などとしておく。

✅「env」の以下を変更する。

# テスト環境用
DB_DATABASE=database_test.sqliteの絶対パス

# 例
# DB_DATABASE=/Applications/MAMP/htdocs/laravel_temp/database/database_test.sqlite

※SQLite以外の場合は適宜データベースに合わせた設定をする必要がある。

💡
本番環境に戻すときは.envをもとに戻す!

【方法3】テスト用データベースを使う(PHPUnitのみ)

PHPUnitのテストのときだけ使うデータベースを設定する方法。

一時的なデータベースでは事足りない場合におすすめ!

✅まず「database」フォルダに「database_test.sqlite」を新規作成する。

✅phpunit.xmlにテスト用のデータベースを設定する。

	<php>
        <server name="APP_ENV" value="testing"/>
        <server name="BCRYPT_ROUNDS" value="4"/>
        <server name="CACHE_DRIVER" value="array"/>
        <!-- <server name="DB_CONNECTION" value="sqlite"/> -->
        <!-- <server name="DB_DATABASE" value=":memory:"/> -->
        <server name="MAIL_MAILER" value="array"/>
        <server name="QUEUE_CONNECTION" value="sync"/>
        <server name="SESSION_DRIVER" value="array"/>
        <server name="TELESCOPE_ENABLED" value="false"/>

				<!-- テスト用のデータベース -->
        <env name="DB_DATABASE" value="database/database_test.sqlite" />
    </php>

💡
これだけでPHPUnitではdatabase_test.sqliteが使われる!

ファイルを新規作成

artisanコマンドでテストファイルを新規作成できる✅

php artisan make:test 〇〇Test

php artisan make:test HelloTest を実行した場合

tests/Feature/HelloTest.php が新規作成される

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class HelloTest extends TestCase
{
    /**
     * A basic feature test example.
     *
     * @return void
     */
    public function test_example()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

💡
コマンドに--unitオプションを付けると、tests/Unitディレクトリ内に新規作成される。

【初級】テストコードを記述

PHPUnitの機能だけで基本的なテストをしてみる。

クラスの中身を以下のように変更する。

tests/Feature/HelloTest.php

class HelloTest extends TestCase
{
    // メソッド名は「test〇〇」であること
    public function testHello()
    {
        // trueかチェック
        $this->assertTrue( true );

        // 空かチェック
        $arr = [];
        $this->assertEmpty( $arr );

        // 等しいかチェック
        $msg = 'Hello';
        $this->assertEquals( 'Hello', $msg );

        // 小さいかチェック
        $n = random_int( 0, 100 );
        $this->assertLessThan( 100, $n );
    }

テストを実行する

以下のコマンドを実行する。

vendor/bin/phpunit

すると以下のようにテストが成功する。

Image in a image block

💡
ここまではLaravelとは関係ないPHPUnitのテスト。
以下で詳しく解説している。

📄 初めてでも分かるPHPUnitの備忘録

【中級】テストコードを記述

Laravelの機能でデータベースを使ったテストをする✅

前提

以下3つが必要。

  • テーブル(例:users)
  • モデル(例:User)
  • Factoryクラス(例:UserFactory)

【補足】Factoryクラスの作り方

artisanコマンドで作成可能✅

php artisan make:factory モデル名

php artisan make:factory Userを実行した場合。

database/factories/UserFactory.php

<?php

namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

class UserFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = User::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            //
        ];
    }
}

definition関数を編集する。

public function definition()
{
    return [
        'name' => $this->faker->name(),
        'email' => $this->faker->unique()->safeEmail(),
        'email_verified_at' => now(),
        'password' => Hash::make('12345678'), // password
        'remember_token' => Str::random(10),
    ];
}

💡
これで準備OK

テストコードを書く

tests/Feature/HelloTest.php

// Userモデルを使う
use App\Models\User;

class HelloTest extends TestCase
{
		// ✅テスト後にデータベースをリセットする
		use RefreshDatabase;

    // メソッド名は「test〇〇」であること
    public function testHello()
    {
				// ✅レコードが0件かチェック
        $this->assertDatabaseCount( 'users', 0 );

				// ✅テストユーザーを生成
				$user = User::factory()->create([
            'name' => 'John',
            'email' => '[email protected]',
            'email_verified_at' => '2023-04-05 20:39:44',
            'password' => Hash::make( '12345678' ),
        ]);

				// ✅レコードが1件かチェック
        $this->assertDatabaseCount( 'users', 1 );
    }

✅テスト後にデータベースをリセットする

use RefreshDatabase;と書くだけでPHPUnitを実行後にデータベースをリセットしてくれる。

毎回リセットすることで前回のテスト結果に影響されることがなくなる。

✅レコードが0件かチェック

$this->assertDatabaseCount( 'users', 0 );でusersテーブルのレコード数を確認している。

0件ならテスト成功!

✅テストユーザーを生成

User::factory()->create(…)でusersテーブルに1件レコードを作成できる。

引数でカラムの値を設定できる。

未設定のカラムはFactoryによって値が設定される。

→引数をすべて省略することも可能!

✅レコードが1件かチェック

ユーザーを追加したのでレコードが増えているはず。

1件ならテスト成功!

💡
テストが終わるとRefreshDatabaseによってレコードが0件に戻る。

テストを実行する

以下のコマンドを実行する。

vendor/bin/phpunit

すると以下のようにテストが成功する。

Image in a image block

【上級】テストコードを記述

会員用ページにログインしてアクセスできるかテストする✅

前提

以下3つが必要。( 📄 Arrow icon of a page link 初心者でもLaravelのユーザー認証が使えるようになる解説 を有効化していること。)

  • テーブル(例:users)
  • モデル(例:User)
  • Factoryクラス(例:UserFactory)

【補足】Factoryクラスの作り方

artisanコマンドで作成可能✅

php artisan make:factory モデル名

php artisan make:factory Userを実行した場合。

database/factories/UserFactory.php

<?php

namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

class UserFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = User::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            //
        ];
    }
}

definition関数を編集する。

public function definition()
{
    return [
        'name' => $this->faker->name(),
        'email' => $this->faker->unique()->safeEmail(),
        'email_verified_at' => now(),
        'password' => Hash::make('12345678'), // password
        'remember_token' => Str::random(10),
    ];
}

💡
これで準備OK

👆中級と同じ

テストコードを書く

tests/Feature/HelloTest.php

use App\Models\User;

class HelloTest extends TestCase
{
		use RefreshDatabase;

    // メソッド名は「test〇〇」であること
    public function testHello()
    {
				// ✅未ログイン状態で'/login'にアクセス
        $response = $this->get( '/login' );
        $response->assertStatus( 200 );         // ステータスコードが200か?

				// ✅ログイン処理
        $user = User::factory()->create();      // テストユーザーを生成
        $this->actingAs( $user );               // ログイン
				// ✅ログインした状態で'/login'にアクセス
        $response = $this->get( '/login' );
        $response->assertStatus( 302 );         // ステータスコードが302か?
    }

✅未ログイン状態で'/login'にアクセス

$this->get( '/login' )でログインページのレスポンスを取得する。

$response->assertStatus( 200 );でステータスコードが200(正常)か確認する。

✅ログイン処理

User::factory()->create();でusersテーブルに1件レコードを作成できる。
ユーザーデータの中身はUserFactoryの内容による。

actingAs( $user );で作成したユーザーでログインする。

✅ログインした状態で'/login'にアクセス

$this->get( '/login' )でログインページのレスポンスを取得する。
コードは最初と同じだが、さっきログイン処理をした点が異なる。

$response->assertStatus( 302 );でステータスコードが302(リダイレクト)か確認する。

【補足】ログインする別の方法

actingAs( $user );ではなく以下でログインすることもできる。

$user = User::factory()->create();      // テストユーザーを生成

// ✅'login'にアクセス + ログイン
$response = $this->post( '/login', [
    'email' => $user->email,
    'password' => '12345678',  // ハッシュ化前のパスワード
] );

$response->assertStatus( 302 );         // ステータスコードが302か?

✅'login'にアクセス + ログイン

$this->post( ‘パス’ , [POST送信するデータ] )でPOST送信できる。

ここではログインページにPOST送信している。

💡
ログインに必要な情報(メールアドレス、パスワード)をPOST送信することでログインできる。

💡
ログインページは「未ログイン状態のときはアクセス可」「ログイン状態のときは別ページにリダイレクトする」となっているかテストしている。

テストを実行する

以下のコマンドを実行する。

vendor/bin/phpunit

すると以下のようにテストが成功する。

Image in a image block

参考サイト

全体的な動作

データベースのリフレッシュ

Laravelで使えるデータベースのassertion