こんにちは、かつコーチです。
前回はロール・権限管理の実装パターンを解説し、認証・セキュリティ編を締めくくりました。
今回からは新章、「テスト」編に入ります。
初回となる今回は、Laravelのテストの土台になっているPHPUnitの基本を扱います。
なぜテストを書く必要があるのか
手動確認の限界
機能を作った後、ブラウザを開いて動作確認する、という進め方は誰もが通る道です。
ただし、この方法には限界があります。
新しい機能を追加するたびに、これまで作った全機能を手動で確認し直すのは現実的ではありません。
私自身、あるプロジェクトで既存の会員登録機能を壊さずに新機能を追加したつもりが、リリース後に「実は登録メールが届かなくなっていた」というバグを出してしまったことがあります。
原因は、共通処理を修正した際に、その処理を使っている別機能への影響を手動確認しきれていなかったことでした。
自動テストが解決すること
自動テストを書いておけば、コードを変更するたびに実行するだけで「壊れていないか」を機械的に確認できます。
人力での確認漏れを防げるだけでなく、修正のたびに感じていた「ここを直したら、あっちが壊れていないか不安」という心理的な負担も減らせます。
PHPUnitとは
PHPのデファクトスタンダードなテストフレームワーク
PHPUnitは、PHPで最も広く使われているテストフレームワークです。
テスト対象のコードを実行し、その結果が期待した値と一致するかを検証する、という仕組みでテストを書いていきます。
Laravelには標準でPHPUnitが組み込まれており、プロジェクトを作成した時点ですでに設定済みの状態になっています。
LaravelにおけるPHPUnitの位置づけ
Laravelのテストは、大きく2種類に分かれます。
- Unit Test:クラス単体のロジックをテストする(データベースやHTTPリクエストを介さない)
- Feature Test:ルーティングからレスポンスまで、機能全体の流れをテストする
どちらもPHPUnitの仕組みの上に成り立っており、Laravel独自の便利なメソッドが追加されている、というイメージです。
Unit TestとFeature Testの詳しい書き方は次回以降で扱うので、今回はまずPHPUnit自体の基本を押さえます。
テストを書いてみる
テストファイルの作成
Laravelではartisanコマンドでテストファイルの雛形を作成できます。
php artisan make:test SampleTest --unit
--unitを付けるとtests/Unit配下に、付けないとtests/Feature配下にファイルが作られます。
最小構成のテスト
まずは中身を理解するために、シンプルなテストを書いてみましょう。
<?php
// tests/Unit/SampleTest.php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
public function test_足し算が正しく計算される(): void
{
$result = 1 + 2;
$this->assertEquals(3, $result);
}
}
test_から始まるメソッド名が、1つのテストケースとして扱われます。
assertEquals(期待値, 実際の値)は「期待値と実際の値が一致しているか」を検証するアサーション(検証メソッド)です。
テストの実行方法
作成したテストは、次のコマンドで実行します。
php artisan test
または直接PHPUnitを実行することもできます。
./vendor/bin/phpunit
すべて成功すると緑色のOK表示、失敗すると赤色で失敗内容が表示されます。
よく使うアサーションメソッド
代表的なアサーション一覧
PHPUnitには数多くのアサーションメソッドが用意されていますが、まずは以下を覚えておけば十分です。
| メソッド | 用途 |
|---|---|
assertEquals($expected, $actual) | 値が等しいか検証 |
assertTrue($condition) | 条件がtrueか検証 |
assertFalse($condition) | 条件がfalseか検証 |
assertNull($value) | 値がnullか検証 |
assertCount($count, $array) | 配列の要素数を検証 |
assertInstanceOf($class, $object) | オブジェクトの型を検証 |
アサーションを使った実例
会員ランクを判定する簡単な関数をテストしてみます。
<?php
function determineRank(int $totalPurchase): string
{
if ($totalPurchase >= 100000) {
return 'ゴールド';
}
return 'レギュラー';
}
<?php
// tests/Unit/RankTest.php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class RankTest extends TestCase
{
public function test_10万円以上でゴールドランクになる(): void
{
$this->assertEquals('ゴールド', determineRank(100000));
}
public function test_10万円未満はレギュラーランクになる(): void
{
$this->assertEquals('レギュラー', determineRank(99999));
}
}
このように「境界値の前後」をそれぞれテストしておくと、条件分岐のミスに気づきやすくなります。
つまずきやすいポイント:1つのテストで色々検証しようとする
失敗時にどこが悪いか分からなくなる
私が最初にテストを書き始めた頃、1つのテストメソッドに複数の検証をまとめて詰め込んでしまい、失敗したときにどこが原因か分からず苦労した経験があります。
❌ Before:1つのテストに複数の観点を詰め込む
<?php
public function test_ランク判定がいろいろ正しい(): void
{
$this->assertEquals('ゴールド', determineRank(100000));
$this->assertEquals('レギュラー', determineRank(99999));
$this->assertEquals('レギュラー', determineRank(0));
}
このテストが失敗しても、テスト名のtest_ランク判定がいろいろ正しいからは、3つの検証のうちどれが失敗したのかがすぐに分かりません。
✅ After:1テスト1観点に分割する
<?php
public function test_10万円以上でゴールドランクになる(): void
{
$this->assertEquals('ゴールド', determineRank(100000));
}
public function test_10万円未満はレギュラーランクになる(): void
{
$this->assertEquals('レギュラー', determineRank(99999));
}
public function test_購入額0円でもレギュラーランクになる(): void
{
$this->assertEquals('レギュラー', determineRank(0));
}
テストを分けておけば、失敗したテスト名を見ただけで「どの条件が壊れているか」がすぐに分かります。
テストコードは、書く手間よりも「後で読んだときに何を保証しているか分かる」ことの方が重要だと考えています。
まとめ
この記事のポイント
- 自動テストは、手動確認では防ぎきれない修正漏れ・デグレを機械的に検出してくれる
- LaravelはPHPUnitを標準搭載しており、
php artisan make:testでテストファイルを作成できる assertEqualsなどのアサーションメソッドで、期待値と実際の値を比較する- 1つのテストメソッドには1つの観点だけを詰め込み、失敗時に原因を特定しやすくする
次に読むべき記事
PHPUnitの基本が分かったところで、次回はLaravel独自の「Feature Test」でコントローラの動作をテストする方法を解説します。
→ 次の記事:Feature Testでコントローラの動作をテストする