LoginSignup
62
43

More than 1 year has passed since last update.

Laravelの2種類のテストの比較(FeatureテストとUnitテスト)

Last updated at Posted at 2021-06-19

はじめに

Laravelのtests/ディレクトリ以下にはFeature/Unit/の二つのディレクトリが自動で作成されます。それぞれのディレクトリにはExampleTest.phpが最初からありますが、これら二つの違いは「素のPHPUnitのTestCaseクラスを継承している」か「Laravel用に拡張されたTestCaseクラスを継承しているか」です。

Feature/ExampleTest.phpはLaravel用に拡張されたTestCaseクラスを継承しているので、DBアクセスを含むLaravelの機能がテスト内で全て使えます。Unit/ExampleTest.phpは素のPHPUnitのTestCaseクラスを継承しているため、Laravelの機能が使えず、素のPHPコードとしてのロジックテストを書くものになっています。

Featureテストは便利なのですが、Laravel用に拡張されている分テストの実行に時間がかかります。なので、Laravelに依存しない形で書けるテストは極力Unitテストにするべきです。

FeatureテストとUnitテストの比較

次のようなUser Modelがあります。isAdmin()メソッドでは、 roleカラムの値が「ADMIN」の場合にtrueを返します。このメソッドについて、FeatureテストとUnitテストを書いて比較します。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    protected $fillable = [
        'name',
        'email',
        'password',
        'role',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    public function isAdmin(): bool
    {
        return $this->role === 'ADMIN';
    }
}

Featureテストの場合

isAdmin()メソッドを呼び出すと、Userモデルは内部でDBアクセスを行い、roleカラムの値をDBから読み出そうとします。なので、Featureテストの場合、実際にDBへデータを登録してからisAdmin()メソッドを呼び出すことになります。

<?php

namespace Tests\Feature;

use App\Models\User;
use Tests\TestCase;

class UserTest extends TestCase
{
    public function test_isAdmin()
    {
        $user = User::factory()->create(['role' => 'ADMIN']);
        self::assertTrue($user->isAdmin());
    }
}

実行結果はこちらです。実行時間は[0.38s]でした。

sail test tests/Feature/UserTest.php 

   PASS  Tests\Feature\UserTest
  ✓ is admin

  Tests:  1 passed
  Time:   0.38s

Unitテストの場合

Unitテストの場合は、DBアクセスができません。では、どうするかというと、Userモデルをインスタンス化した後に、roleプロパティへ自分でデータをセットします。

<?php

namespace Tests\Unit;

use App\Models\User;
use PHPUnit\Framework\TestCase;

class UserTest extends TestCase
{
    public function test_isAdmin()
    {
        $user = new User();
        $user->role = 'ADMIN';

        // これでもOK
        // $user = new User(['role' => 'ADMIN']);

        self::assertTrue($user->isAdmin());
    }
}

実行結果はこちらです。実行時間は[0.06s]でした。Featureテストの1/6程度の時間で終わっています。

sail test tests/Unit/UserTest.php

   PASS  Tests\Unit\UserTest
  ✓ is admin

  Tests:  1 passed
  Time:   0.06s

まとめ

今回はFeatureテストとUnitテストの実行時間の比較を行いました。テスト対象のコードはとてもシンプルなものでしたが、それでも6倍以上の差でUnitテストの方が高速ということがわかりました。プロジェクトの全てのテストをFeatureテストにしてしまうと、少なくともこれだけ実行時間に差が出てしまうということですね。
また、実際のFeatureテストでは、裏でLaravelがよしなにやってくれていることも含めると、更に多くのLaravelの機能を使うことになると思います。そう考えるとFeatureテストとUnitテストでは6倍以上の実行時間の差が出るのではと思います。みなさん、Unitテスト書いていきましょう。そしてUnitテストが書けるような設計をしましょう。

62
43
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
62
43