Laravel 11 PHPUnit を使ったテストの方法
いつもご利用ありがとうございます。
この記事には広告が掲載されており、その広告費によって運営しています。
Laravel 11 PHPUnit を使ったテストの方法についてまとめています。
はじめに
PHPUnit を使ったテストは簡単に誰でも書くことができます。
決して難しいものでは無いので、「テストコードを書くのは難しそうだからチャレンジするのは止めよう」と思わず、とりあえずこの記事を通して1個簡単なテストを実行してみてください。
難しいと思っていても最初1個動かすだけで全然「書ける、書けるぞ」と思ったのを覚えています。
ちなみに、テストは 100 点のコードを書くのがとてつもなく難しいと思ってます。
誰でもボールは蹴れるけど、ケビン・デブライネにはなれないということです。
PHPUnit を動かす前に
テストの設定ファイルを確認する
/phpunit.xml
というファイルに PHPUnit の設定が書かれているので、中身を確認してみてください。
テスト用のデータベースを使う
テスト用のデータベースを使う理由は、現在使われている開発用のデータベースに影響が出てしまうためです。
開発用のテストデータを入れ直したりするのが大変なので、まずはテスト用のデータベースに接続するように以下のように書き直します。
そこに、コメントアウトされた以下 の記述があるので、
<!-- Before -->
<!-- <env name="DB_CONNECTION" value="sqlite"/> -->
<!-- <env name="DB_DATABASE" value=":memory:"/> -->
<!-- After -->
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
とりあえずこの設定にします。
テストを実行する
コマンドを打って実行します
php artisan test
または、
./vendor/bin/phpunit
このコマンドで実行します。新しいバージョンの Laravel では最初のコマンドで動きます。
古いバージョンの Laravel では2つめのコマンドじゃないと動きません。
実行結果を見る
php artisan test
PASS Tests\Unit\ExampleTest
✓ that true is true
Tests: 0 skipped, 1 passed (1 assertions)
Duration: 0.66s
上記のようなログが流れれば OK です。
赤の文字が出なければ大丈夫です。おめでとうございます。
テストコードを書く時に知っておくべきこと
今回は本当に初めて知る人向けに、最初に知っておくべき要点に絞って解説していきます。
テストコードを書いておくことで、今回実装するコードが正常に動作することを確認しつつ、それがこれまでに実装してきた機能に作用して別のバグを作っていないか確認することができます。
機能が増えれば増えるほど、人の手と目でチェックすることは大変なので、テストをコーディングすることで自動化します。
テストコードの種類
- 機能テスト
- 単体テスト
Laravel での PHPUnit を使ったテストではこの2つを知っておけばよいでしょう。
機能テストは Feature テスト、単体テストは Unit テストと呼ばれます。
Feature テストとは?
機能テストは、機能に対してテストを行うものです。
Laravel で言えばルーティングに対してテストを行うと言って 変じゃないと思います。
例えば、以下の例では/
のルーティングに対して正常にステータス 200 を返されているかチェックしています。
public function test_a_basic_request()
{
$response = $this->get('/');
$response->assertStatus(200);
}
Unit テストとは?
単体テストは、それよりも細かい関数ごとのテストです。
関数が期待した結果を返すことをテストし、他のものに依存せず独立したテストケースにすることが基本です。
Calculater
というクラスを作成し、簡単な足し算をする関数を作ります。
<?php
class Calculator
{
public function add($a, $b)
{
return $a + $b;
}
}
この簡単な足し算の関数をテストしていきましょう。
CalculatorTest.php
<?php
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
public function testAdd()
{
// Calculatorクラスのインスタンスを作成
$calculator = new Calculator();
// テスト対象メソッドの結果を検証
$this->assertEquals(4, $calculator->add(2, 2)); // 2 + 2 = 4
$this->assertEquals(0, $calculator->add(-2, 2)); // -2 + 2 = 0
$this->assertEquals(5, $calculator->add(3, 2)); // 3 + 2 = 5
}
}
assertEquals
は、期待された4
と同じになったかどうかを判定し、違っていればエラーを出し、あっていればpassed
の数字が増えます。
独学の場合、単体テストはピンと来ないですよね、分かります。
ですので、とりあえず Feature テストを実装して、慣れていきましょう。
Feature テストを書く
ホームにアクセスするテストを書く
早速/
にアクセスするテストを書いてみましょう。
コマンドでファイル作成
テスト用のファイルは、コマンドで作成します。
ファイル名は、統一性があればなんでも良いと思いますが今回はコントローラー名でいきたいと思います。
php artisan make:test HomeControllerTest
注意点は、HomeControllerTest
というように末尾をTest
にします。
そうすることでテストファイルと認識してくれます。
コマンドがうまくいくと、/tests/Feature/HomControllerTest.php
というファイルが作成されます。
あとはこのファイルに、何を書くかです。
/tests/Feature/HomControllerTest.php に書く
関数名は、test
で始める必要があります。test_example
または、testExample
になります。
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class HomeControllerTest extends TestCase
{
/**
* A basic feature test example.
*/
public function test_example(): void
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
テストの実行コマンド
とりあえずこのファイルを実行してみます。
php artisan test --filter=HomeControllerTest
上記のようにfilter
オプションで特定のテストを実行することができます。
PASS Tests\Feature\HomeControllerTest
✓ example 0.81s
Tests: 1 passed (1 assertions)
Duration: 1.15s
今回実行したテストは、
public function test_example(): void
{
$response = $this->get('/');
$response->assertStatus(200);
}
/
にアクセス- レスポンスがステータスを 200
assert している回数が1回なので、結果も 1 assertions になっています。
使用することができる assert の一覧は下の URL で確認できます。
11.x HTTP テスト - Laravel #利用可能なアサート
仕様を追加してテストする
例えばこのホ ームでは投稿の一覧が掲載されているとします。
public function index()
{
$posts = Post::latest()->take(20)->get();
return view('home',compact('posts'));
}
Post モデルの最新のレコードを 20 件取得しています
これをテストしてみます。
Factory クラスでテストデータを作る
まずは、テストデータを入れるためのFactoryクラス
を用意します。
Post モデル
に対してテストデータを作成します。
php artisan make:factory PostFactory --model=Post
database/factories/PostFactory.php
に書きます。
public function definition(): array
{
return [
'title' => 'タイトル',
'content' => 'コンテンツ'
];
}
faker を使った書き方など詳しい Factory の使い方はこちら
Laravel で Factory を使ってテストデータを作成する方法
Factory クラスを作ることで、以下のテストコードの
Post::factory()->count(25)->create();
が使えるようになります。
/tests/Feature/HomControllerTest.php を書き直す
それでは、/tests/Feature/HomControllerTest.php
に、
Post モデルの最新のレコードを 20 件取得しているか確認するテストを書いてみましょう。
use App\Models\Post;
//中略
public function test_ホーム()
{
// テストデータとして、25件の投稿を作成
Post::factory()->count(25)->create();
// GETリクエストで `/` にアクセス
$response = $this->get('/');
// ステータスコードが200であることを確認
$response->assertStatus(200);
// ビューが受け取った `$posts` の内容を確認
$response->assertViewHas('posts', function ($posts) {
return $posts->count() === 20; // 投稿数が20件であることを確認
});
// `$posts` の内容が最新の20件の投稿であることを確認
$latestPosts = Post::latest()->take(20)->get();
$response->assertViewHas('posts', $latestPosts);
}
一つの関数に複数のassert
を書くことができます。
これを実行すると、
PASS Tests\Feature\HomeControllerTest
✓ example 0.48s
Tests: 1 passed (24 assertions)
Duration: 0.65s
成功しました。
次はわざと間違えてみます。
$latestPosts = Post::latest()->take(19)->get();
$response->assertViewHas('posts', $latestPosts);
Failed asserting that actual size 20 matches expected size 19.
このようにエラーが出ました。
なんとなくノリがわかってきましたか?
Unit テストを書く
難しくなります。
今回テストする関数
class UserManager
{
protected $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function registerUser(array $userData): bool
{
// ユーザーが既に存在するかチェック
if ($this->userRepository->exists($userData['email'])) {
throw new RuntimeException('このメールアドレスは既に使用されています。');
}
// ユーザー登録
$user = $this->userRepository->create($userData);
return true;
}
}
テストコード
use PHPUnit\Framework\TestCase;
class UserManagerTest extends TestCase
{
private $userRepository;
protected function setUp(): void
{
// Mockを作成
$this->userRepository = $this->createMock(UserRepository::class);
$this->userManager = new UserManager($this->userRepository);
}
public function testRegisterUserSuccessfully()
{
// ダミーデータ
$userData = [
'email' => 'test@example.com',
'password' => 'password123',
];
// Mockの振る舞い設定
$this->userRepository->expects($this->once())
->method('exists')
->with($this->equalTo($userData['email']))
->willReturn(false);
$this->userRepository->expects($this->once())
->method('create')
->with($this->equalTo($userData))
->willReturn((object) $userData);
// メソッドの呼び出しとアサーション
$result = $this->userManager->registerUser($userData);
$this->assertTrue($result);
}
public function testRegisterUserWithExistingEmail()
{
$userData = [
'email' => 'existing@example.com',
'password' => 'password123',
];
// 既存ユーザーを示す振る舞い
$this->userRepository->expects($this->once())
->method('exists')
->with($this->equalTo($userData['email']))
->willReturn(true);
// 例外が投げられるかチェック
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('このメールアドレスは既に使用されています。');
$this->userManager->registerUser($userData);
}
}
とても難しいのですが、やっていることとしては、関数registerUser()
をテストしています。
- メールアドレス重複した際にエラーメッセージを出力
- 問題なければユーザーを登録
というのをテストしています。
難しいので、クラスや関数の理解が深まったタイミングでチャレンジするのが良いと思います。
理解を深めるために知っておくと良いものリスト
- Factory によるテストデータ作成
- assert の種類
- setUp()
- tearDown()
- Mock
他にもあるかもしれないのですが、思いついたのは以上です。
単語使って検索をかけると色々調べられると思います。
以上です。
自分も良いテストがかけるように頑張っていきたいと思っています。
誰かの参考になれば幸いです。