N_OR’s diary

無職が何か言ってる

Laravelでphpunitを利用したテスト

phpunitでテストを書いてみる。

テストの自動化というか、DevOpsというか、システム作りもシステム化していくべきで、こういう対応は大事だと思う。

ただ、テストを自動化することの是非について、仕事としての判断が入ってくると喧嘩する人たちが出てくるので注意が必要。

個人的にはテストコードを書かなくて良いのは、一回だけ動けばいいようなプログラムくらいだと思う。(ただし、テスト自体は必要)
そうではないプログラムのテストを書かない方が良いと言う人は、真面目にテストしてなくてテストの大変さを知らないんじゃないかと思う。

あと、テストコードを必須にすれば、コードを書き逃げするプログラマに対しての牽制にもなりそうかな。

phpunitの確認

Laravelには標準でphpunitが入っているらしいので、とりあえずバージョン確認。

phpunit --version
PHPUnit 9.5.6 by Sebastian Bergmann and contributors.

9.5.6だった。

とりあえず動かしてみる。 実行は

phpunit 

で。

結果は

PHPUnit 9.5.6 by Sebastian Bergmann and contributors. 
 
Warning:       Your XML configuration validates against a deprecated schema. 
Suggestion:    Migrate your XML configuration using "--migrate-configuration"! 

..                                                                  2 / 2 (100%) 

Time: 00:00.155, Memory: 18.00 MB 

OK (2 tests, 2 assertions) 

何かOKでた。

デフォルトで何が動いている?

ここら辺が動いている模様。

app/tests/Feature/ExampleTest.php 
app/tests/Unit/ExampleTest.php 

これらはlaravelにデフォルトでおかれているファイル。
動作させるフォルダやディレクトリの設定は

app/phpunit.xml 

にある。
クラス名にTestついてなきゃだめ、とかそんなルールっぽいのが書いてる。

テストの作成

phpunitで動作させるクラスを作成する。 laravelでのコマンド

php artisan make:test HomeTest

上記コマンドではデフォルトでFeatureディレクトリにファイルを作成する。 --unitオプションでUnitディレクトリに作成。 FeatureとUnitの使い分けは、

  • Featureは機能のテスト
  • Unitはメソッドのテスト

のイメージかしら。 ここら辺の使い方は開発時にルールを決めた方がよさげ。

なお、クラス名は最後「Test」(ファイル名が◯◯Test.php)で作る必要あり。

テストクラスの実装

ログイン後にhomeへのリクエストが機能しているかを確認するためのテスト。 下記は、テストユーザを作って、そのユーザでログインし、認証されたユーザで正常にhomeのコンテンツが返されるか、を確認している。

<?php

namespace Tests\Feature;

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

/**
 * テストデータ
 */
class TestData
{
    public const email = 'user1@example.com';
    public const password = 'Test1234';
}

/**
 * ホームコントロールテスト
 */
class HomeTest extends TestCase
{
    // データベースの初期化にトランザクションを使う
    use DatabaseTransactions;

    // ログインユーザの保持用
    public $TestUser;

    /**
     * 初期設定
     *
     * @return void
     */
    public function setUp(): void
    {
        parent::setUp();

        // `users` テーブルにデータを作成 
        $this->TestUser = factory(App\User::class)->create([
            'id' => '999',
            'name' => 'test太郎',
            'password' => bcrypt(TestData::password),
            'email' => TestData::email,
        ]);
    }

    /**
     * テストユーザでのログイン
     *
     * @return void
     */
    public function Login()
    {
        // ログインページへの単純なアクセス
        $response = $this->get(route('login'));
        $response->assertStatus(200);

        // ログイン実施
        $response = $this->post(route('login'), [
            'email' => TestData::email,
            'password' => TestData::password,
        ]);
        // リダイレクトでページ遷移するのでstatusは302
        $response->assertStatus(302);
        // リダイレクト先のパス
        $response->assertRedirect('/home');

        // ユーザーがログイン認証されているか
        $this->assertAuthenticatedAs($this->TestUser);
    }

    /**
     * ログインのテスト
     *
     * @return void
     */
    public function testIndex()
    {
        // ログイン
        $this->Login();

        // ログイン後のhome
        $response = $this->get(route('home'));
        $response->assertStatus(200);
    }   
}

assertについて

望んだ値かをチェックするためメソッド。色んな種類があるので、状況に応じて使い分ける。

factoryについて

テストデータを作るもの。 ファイルは以下に置かれる。

database/factories

ちなみに、

php artisan ui vue –auth  

上記のartisanコマンドでユーザ認証機能などを作成している場合、UserのFactoryはデフォルトで作成されている。

テスト実施時にpostのレスポンスコードが419

419エラーコードはCSRFでのエラーらしい。
テストの時は無視するように設定。

以下のファイルを開き、

App/Middleware/VerifyCsrfToken.php 

以下のコードを追加。

public function handle($request, \Closure $next) 
{ 
    if (env('APP_ENV') !== 'testing') { 
        return parent::handle($request, $next); 
    } 
    return $next($request); 
} 

testingとかその辺の理由は

app/phpunit.xml 

あたりを覗くとわかりそうな気がしないでもない