4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

LaravelでPHPUnitで自動テストを行う

Last updated at Posted at 2023-04-23

個人メモ。

環境

  • Laravel 10.6.2
  • PHP 8.2.0
  • Vagrant CentOS7

準備

  • PHPUnitの存在を確認しておきます。
    Laravelルートで、下記のように出てくればOK。
$ vendor/bin/phpunit --version
PHPUnit 10.0.19 by Sebastian Bergmann and contributors.

動作確認。
laravelルートに移動して、デフォルトで置いてある
tests/Unit/ExampleTest.php
を動かしてみる。

$ vendor/bin/phpunit tests/Feature/ExampleTest.php
PHPUnit 10.0.19 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.2.0
Configuration: E:\vagrant\Vagrant2304\centos74\Laravelapp\laravelapp\phpunit.xml
.                                                                   1 / 1 (100%)
Time: 00:02.099, Memory: 24.00 MB
OK (1 test, 1 assertion)

動作OK。これは、これは、ルート/にアクセスして、存在あり:ステータス200を返すかどうかのテスト。

\ExampleTest.php の中身の一部

ExampleTest.php
    public function test_the_application_returns_a_successful_response(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
  • phpunit.xml
    phpunit.xmlは、テストの環境設定ファイルで、デフォルトでは
    ./tests/Unit
    ./tests/Feature
    フォルダのテストを行うようになっているが、今回は単独ファイル1つのみを指定してテストを行う予定なので、このファイルは修正しない。

  • 使用するDBを複製し、テスト用DBを作成しておきます。
    (データの削除、変更などを行うため、専用のテスト用DBをmigrateなどを使用して作成しておく)

  • Laravelルートの.envファイルをコピーし、.env.testingファイルを作ります。これはテスト実行時の環境設定ファイルになります。

テスト: テストの準備 10.x Laravel

プロジェクトのルートに.env.testingファイルを作成することもできます。このファイルは、PHPUnitテストを実行するとき、または--env=testingオプションを指定してArtisanコマンドを実行するときに、.envファイルの代わりに使用されます。

.env.testingを、テスト環境で使うデータベース設定に書き換えます。phpunit実行時には、この.env.testingの設定が使われます。

.env.testing
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
  • ファクトリーの作成
    テスト時にデータを追加するファクトリを作ります。
    Userのダミーデータを作るfactoryはあらかじめあるので、今回はこれを使います。
    \database\factories\UserFactory.php の中身は下記のようになっています。
\database\factories\UserFactory.php
public function definition(): array
{
    return [
        'name' => fake()->name(),
        'email' => fake()->unique()->safeEmail(),
        'email_verified_at' => now(),
        'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
        'remember_token' => Str::random(10),
        'img' => 'noimage.jpg',
    ];
}

他のテーブルでもfactoryを使う場合には、php artisan make:factory コマンドで作成しておきます。
(今回は詳細は略)

  • テストファイル作成
    php artisan make:test コマンドでテストファイルを作成。今回はTodoTestという名前で作成。
    ※デフォルトでは、テストはtests/Featureディレクトリへ配置され、今回の場合には \tests\Feature\TodoTest.php が作られます。
# php artisan make: test TodoTest
   INFO  Test [tests/Feature/TodoTest.php] created successfully.
  • ページのステータス確認
    では、作られたTodoTest.phpを編集して、assertStatusを呼び出す簡単なテストを作ってみます。
    assertStatus は、レスポンスに指定HTTPステータスコードがあることを確認するコマンドです。
    参考:HTTPテスト 10.x Laravel
    ここで
    assertStatus(200)は未ログイン状態でページが見えることを確認。
    assertStatus(302)は未ログイン状態ではページが入れないことを確認。
TodoTest.php
namespace Tests\Feature;

use Tests\TestCase;
use App\Models\User;
use App\Models\Todo;
use Illuminate\Support\Facades\Hash;

// ページのステータス確認
public function test_todo_status(): void
{
    //ログインしなくても見れる確認
    $response = $this->get('/todo');
    $response->assertStatus(200);   
    $response = $this->get('/todo/signup');
    $response->assertStatus(200);   
    $response = $this->get('/todo/login');
    $response->assertStatus(200);   
    $response = $this->get('todo/mypage_resign_logout');
    $response->assertStatus(200);   
    $response = $this->get('todo/forget_password');
    $response->assertStatus(200);   

    //ログインしないと入れない状態である確認
    $response = $this->get('/todo/logout');
    $response->assertStatus(302);   
    $response = $this->get('/todo_detail');
    $response->assertStatus(302);   
    $response = $this->get('/todo/new');
    $response->assertStatus(302);   
    $response = $this->get('/todo/edit');
    $response->assertStatus(302);   
    $response = $this->get('/todo/del');
    $response->assertStatus(302);   
    $response = $this->get('/todo/mypage');
    $response->assertStatus(302);   
    $response = $this->get('/todo/mypage_pass');
    $response->assertStatus(302);   
    $response = $this->get('/todo/mypage_resign');
    $response->assertStatus(302);   

}

テストファイルを作成したら、実行は、php artisan testコマンドに続けてファイル名を指定することで、テストが呼び出される。

#  php artisan test tests/Feature/TodoTest.php

   PASS  Tests\Feature\TodoTest
  ✓ todo status                             0.12s  
  Tests:    1 passed (13 assertions)
  Duration: 0.21s

このようにテストが成功した。
万が一エラーが出た場合には、コードを修正していく。

続けてテストを記載していく。

  • マイページが表示できるかのテスト
TodoTest.php
// マイページが表示できるか
public function test_todo_mypage_show(): void
{
    // ユーザーを1人作成し、そのユーザーが マイページを開いて、ステータスが200(無事表示できた)ことを確認
    $user = User::factory()->create();
    $response =  $this->actingAs($user)->get('/todo/mypage');
    $response->assertStatus(200);

    // テストデータ削除
    User::where('id', $user->id)->delete();
}

User::factory()->create(); でユーザーを1人作成。
そのユーザーが、ログインが必要なマイページにアクセスできるかを、assertStatus(200)でテスト。
今回、毎回最後に
User::where('id', $user->id)->delete();
で毎回該当のユーザーデータを削除している。
※テストファイル上部に
use RefreshDatabase;
と入れておくと、毎回データを削除してくれるらしいのだが、既存のデータもすべて消えるとのことで、今回は追加したデータのみ、その都度削除している。
参考:データベースにテストデータが保存されるのを避ける方法

  • データ追加テスト
TodoTest.php
// todo追加
public function test_can_new_todo(): void
{
    $user = User::factory()->create();

    $response =  $this->actingAs($user)->get('/todo/new');
    $response->assertStatus(200);

    $response = $this->actingAs($user)->post('/todo/new', [
        'title' => 'テスト投稿',
        'todo' => 'テスト投稿をします',
    ]);

    $this->assertDatabaseHas('todo', [
        'title' => 'テスト投稿',
        'todo' => 'テスト投稿をします',
        'user_id' => $user->id,
    ]);        

    // テストデータ削除
    Todo::where('user_id', $user->id)->delete();
    User::where('id', $user->id)->delete();
}

これは、User::factory()->create()でユーザーを作成、
assertStatus(200)でページが表示されることを確認。
$this->actingAs($user)->postでtodoテーブル(ユーザーテーブル1に対し多のリレーション関係にある)にデータ追加、
$this->assertDatabaseHasでtodoテーブルにデータ追加されているかの確認。
最後に、今回追加したテストデータ削除。

  • データ編集テスト
TodoTest.php
// todo編集
public function test_can_edit_todo(): void
{
    $user = User::factory()->create();

    // 投稿を作成
    $response = $this->actingAs($user)->post('/todo/new', [
        'title' => 'テスト投稿',
        'todo' => 'テスト投稿をします',
    ]);

    // 投稿を変更
    $todo = Todo::where('user_id', $user->id)->first();
    $todo_id = $todo->id;
    $response = $this->actingAs($user)->post('/todo/edit?id=' . $todo_id, [
        'title' => 'テスト投稿 変更',
        'todo' => 'テスト投稿を変更します',
    ]);
    // 変更したデータが存在することを確認
    $this->assertDatabaseHas('todo', [
        'title' => 'テスト投稿 変更',
        'todo' => 'テスト投稿を変更します',
        'user_id'=> $user->id,
    ]);

    // テストデータ削除
    Todo::where('user_id', $user->id)->delete();
    User::where('id', $user->id)->delete();
}

これは、User::factory()->create()でユーザーを作成、
まずデータを追加後、追加したデータを変更。
$this->assertDatabaseHasでtodoテーブルのデータが変更されているか確認。
最後に、今回追加・変更したテストデータ削除。

  • データ削除
TodoTest.php
// todo削除
public function test_can_del_todo(): void
{
    $user = User::factory()->create();

    // 投稿を作成
    $response = $this->actingAs($user)->post('/todo/new', [
        'title' => 'テスト投稿',
        'todo' => 'テスト投稿をします',
    ]);

    // 投稿を削除
    Todo::where('user_id', $user->id)->delete();
    // 変更したデータが存在しないことを確認
    $this->assertDatabaseMissing('todo', [
        'title' => 'テスト投稿',
        'todo' => 'テスト投稿をします',
        'user_id'=> $user->id,
    ]);

    // テストデータ削除
    Todo::where('user_id', $user->id)->delete();
    User::where('id', $user->id)->delete();
}

これは、User::factory()->create()でユーザーを作成、
まずデータを追加後、追加したデータを削除。
$this->assertDatabaseMissingでtodoテーブルに該当データが存在しないことを確認。
最後に、今回追加したテストデータ削除。

  • ログアウト
TodoTest.php
// todoログアウト
public function test_todo_logout(): void
{

    // 未ログイン状態でマイページにアクセスできない
    $response =  $this->get('/todo/mypage');
    $response->assertStatus(302);

    // ユーザーを1人作成し、そのユーザーがマイページを開いて、ステータスが200(無事表示できた)ことを確認
    $user = User::factory()->create();
    $response =  $this->actingAs($user)->get('/todo/mypage');
    $response->assertStatus(200);
    // ユーザーが認証済み
    $this->assertAuthenticatedAs($user);

    // ログアウト後に再度マイページを表示して、表示されないことを確認
    $response =  $this->actingAs($user)->get('/todo/logout');
    // ログインページにリダイレクト
    $response->assertStatus(302);
    $response->assertRedirect('/todo');
    // ログアウト後に認証されていないことを確認
    $this->assertGuest();

    // テストデータ削除
    User::where('id', $user->id)->delete();

}

これは、未ログイン状態でマイページにアクセスできないことを確認、User::factory()->create()でユーザーを作成し、マイページを開いて表示できることを確認。
ユーザーが認証済であることをassertAuthenticatedAsで確認。
次に、ログアウト後に再度マイページを表示して、表示されないことを確認。ログアウト後に認証されていないことをassertGuestで確認。
最後に、テストデータ削除。

  • マイページ、ユーザー情報変更
TodoTest.php
// マイページ ユーザー情報変更
public function test_todo_mypage_change(): void
{
    // ユーザー作成
    $user = User::factory()->create();

    // 投稿を変更(name,email)
    $user_name = $user->name;
    $response = $this->actingAs($user)->post('/todo/mypage', [
        'name' => $user_name . '★',
        'email' => 'testuser@gmail.com',
    ]);
    $response->assertStatus(200);

    // 変更したデータが存在することを確認
    $this->assertDatabaseHas('users', [
        'id'=> $user->id,
        'name'=> $user_name . '★',
        'email' => 'testuser@gmail.com',
    ]);
    // テストデータ削除
    User::where('id', $user->id)->delete();
}

これは、ユーザーを作成し、ユーザーのnameを変更、その後変更したデータが存在することをassertDatabaseHasで確認、その後テストデータ削除。

  • パスワード変更
TodoTest.php
// パスワード変更
public function test_todo_password_edit(): void
{
    // ユーザー作成
    $user = User::factory()->create();

    // 投稿を変更
    $password = '12345678'; //変更後パス
    $response = $this->actingAs($user)->post('/todo/mypage_pass', [
        'current_password' => 'password',   //factoryで生成するpass
        'user_password' => $password,
        'confirm_password' => $password,
    ]);
    $response->assertStatus(200);
    
    $response->assertSeeText('更新しました。');

    // ユーザー情報を変更したのでrefresh()を使って値を更新する
    $user->refresh();
    // 変更したパスワードがユーザーのパスワードと一致しているか確認
    $this->assertTrue(Hash::check($password, $user->password));

    // テストデータ削除
    User::where('id', $user->id)->delete();

}

これは、ユーザーを作成し、ユーザーのパスワードを変更。
その後、変更したパスワードが現在のユーザーのパスワードと一致しているか確認。

  • パスワード変更 5文字以上でなければエラー
TodoTest.php
// パスワード変更 5文字以上でなければエラー
public function test_todo_password_edit_5char(): void
{
    // ユーザー作成
    $user = User::factory()->create();

    // 投稿を変更
    $password = '1111'; //変更後パス
    $response = $this->actingAs($user)->post('/todo/mypage_pass', [
        'current_password' => 'password',   //factoryで生成するpass
        'user_password' => $password,
        'confirm_password' => $password,
    ]);
    
    $response->assertSessionHasErrorsIn("新しいパスワードは、5文字以上で入力してください。");

    // テストデータ削除
    User::where('id', $user->id)->delete();

}

これは、ユーザー作成し、バリデーションが5文字以上でなければエラーのところ、4文字でパスワードを更新しようとします。
assertSessionHasErrorsInで求めるエラー文が含まれているか確認します。

  • 最後に動作確認
    すべてのテストを実行してみます。途中でエラーが起こったところもあるのですが、ソースを修正することで、テストがすべて通過するようになりました。
# php artisan test tests/Feature/TodoTest.php

   PASS  Tests\Feature\TodoTest
  ✓ todo status                          1.36s
  ✓ todo mypage show                     0.37s
  ✓ can new todo                         0.30s
  ✓ can edit todo                        0.21s
  ✓ can del todo                         0.15s
  ✓ todo logout                          0.19s
  ✓ todo mypage change                   0.40s
  ✓ todo password edit                   0.32s
  ✓ todo password edit 5char             0.16s

  Tests:    9 passed (35 assertions)
  Duration: 3.93s

参考

テスト: テストの準備 10.x Laravel
データベーステスト 10.x Laravel
HTTPテスト 10.x Laravel

LaravelでPHPUnitを使ってCRUD処理のテストを自動化①【投稿の作成と保存テスト】
LaravelでPHPUnitを使ってテストする手法
Laravelでログイン関係のテストをしてみる
LaravelでEmailとPassword変更についてのテストを書いてみた

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?