はじめに
これまでテストコードに触れたことがなかったのですが、今回実務で扱う機会があったため、初めて勉強してみる事にしました。その学習の過程をまとめたのが本記事です。
PHPUnitとは?
PHPUnitは、PHPで単体テスト(ユニットテスト)を実行するためのフレームワークです。ユニットテストは、個々の関数やメソッドが意図した通りに動作するかを確認するためのテストです。PHPUnitを使うことで、バグの発見やコードのリファクタリングをより安全に行うことができます。
この記事で学べること
PHPUnitの基本的な書き方や、ユニットテストの方法が学べます。
実際のテストの具体例を見てテストとはどういうものなのか?をざっくりと把握できます。
動作環境
- OS:macOS Sequoia 15.1.1
- PHP:8.4.4
- PHPUnit:12.1.4
- Composer:2.5.1
PHPUnitのインストールに必要なこと
公式では、「PHPUnit10にはPHP8.1が必要」との記載があり、出来れば最新バージョンのPHPを使用する方が良いみたいです。
公式ではPHPの設定ファイルに以下を推奨されているので設定します。
error_reporting=-1
xdebug.show_exception_trace=0
xdebug.mode=coverage
zend.assertions=1
assert.exception=1
memory_limit=-1
インストール方法
Composerを使ったインストール手順
composer require --dev phpunit/phpunit
以下のディレクトリが作成されます。
├── vendor
├── composer.json
└── composer.lock
以下コマンドでバージョンの確認ができます。
./vendor/bin/phpunit --version
PHPUnit 12.1.4 by Sebastian Bergmann and contributors.
composer.jsonファイルにオートローディングの設定を追加
{
"require-dev": {
"phpunit/phpunit": "^12.1"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
}
}
オートローダーを更新するコマンドを実行
composer dump-autoload
ここまでの作業で実際にPHPUnitが動くようになります。
PHPUnitの基本的な使い方
今回テストするクラス
今回は引数に渡された値で挨拶を返すクラスをテストします。
<?php declare(strict_types=1);
namespace App;
final class Greeter
{
public function greet(string $name): string
{
return 'Hello, ' . $name . '!';
}
}
テストケースの作成
testsディレクトリ配下にGreeterTest.phpを作成
<?php declare(strict_types=1);
namespace App\Tests;
use PHPUnit\Framework\TestCase; //TestCaseを使用
use App\Greeter;
//TestCaseを継承させる事でテスト可能
final class GreeterTest extends TestCase
{
public function testGreetsWithName(): void
{
$greeter = new Greeter;
// 'Hello, Alice!'が$greetingに入る
$greeting = $greeter->greet('Alice');
// 第一引数の値と第二引数の値と型が等しいかをチェック
$this->assertSame('Hello, Alice!', $greeting);
}
}
テストの実行
以下コマンドでGreeterTest.phpのテストを実行します。
./vendor/bin/phpunit tests/GreeterTest.php
// tests/ディレクトリ以下を全て実行する場合
./vendor/bin/phpunit
テスト結果
.
が返ってきているのでテストは成功となります。
PHPUnit 12.1.4 by Sebastian Bergmann and contributors.
Runtime: PHP 8.4.4
. 1 / 1 (100%)
Time: 00:00.020, Memory: 8.00 MB
OK (1 test, 1 assertion)
テストが失敗した時の挙動も見てみる
先程のテストケースを失敗させてみます。
<?php declare(strict_types=1);
namespace App\Tests;
use PHPUnit\Framework\TestCase; //TestCaseを使用
use App\Greeter;
final class GreeterTest extends TestCase
{
public function testGreetsWithName(): void
{
$greeter = new Greeter;
$greeting = $greeter->greet('Ken');
// $greetingの値が'Hello, Ken!'になり、等しくないのでエラーになる
$this->assertSame('Hello, Alice!', $greeting);
}
}
先程とは違い記号がF
になっており、エラーになっている事が分かります。
エラーの発生したテストメソッドや、期待値と実際の値等も表示されます。
PHPUnit 12.1.4 by Sebastian Bergmann and contributors.
Runtime: PHP 8.4.4
F 1 / 1 (100%)
Time: 00:00.040, Memory: 8.00 MB
There was 1 failure:
1) App\Tests\GreeterTest::testGreetsWithName
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'Hello, Alice!'
+'Hello, Ken!'
/Users/ユーザー名/Desktop/PHPUnit2/tests/GreeterTest.php:15
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
PHPUnitの出力記号一覧
色々ありますが、.
,F
,E
だけ覚えておけば事足りそうです。
記号 | 意味 | 説明 |
---|---|---|
. |
成功 | 問題のないテストが正常に終了した |
F |
失敗 | アサーションが失敗した |
E |
エラー | テスト中に例外や致命的なエラーが発生した |
W |
警告 | テストが警告を発した |
R |
危険 | テストが危険と判断された(例:アサーションなし) |
D |
非推奨 | 非推奨の機能を使用した |
N |
通知 | テストが通知をトリガーした |
I |
未完了 | テストが未完了としてマークされた |
S |
スキップ | テストが実行されずスキップされた |
PHPUnitでよく使うアサーション
アサーション | 意味・用途 |
---|---|
assertEquals($expected, $actual) |
値が等しい(型は厳密でなくてOK) |
assertSame($expected, $actual) |
値と型の両方が等しい(厳密比較) |
assertTrue($condition) |
条件が true であること |
assertFalse($condition) |
条件が false であること |
assertNull($value) |
値が null であること |
assertNotNull($value) |
値が null ではないこと |
assertEmpty($value) |
値が空("" , 0 , [] など)であること |
assertNotEmpty($value) |
値が空ではないこと |
assertCount($expectedCount, $array) |
配列やカウント可能なオブジェクトの要素数を検証 |
assertInstanceOf($class, $object) |
オブジェクトが特定のクラスのインスタンスであること |
assertStringContainsString($needle, $haystack) |
部分文字列が含まれているか |
assertThrows(Exception::class) (PHPUnit 10)
|
指定の例外がスローされること(新構文) |
まとめ
今回は公式ドキュメントを参考に、PHPUnitを学習してみました。
実務でテストコードを初めて見た際は、何が何だか分からなかったですが今回で少しは理解できたかと思います。
今回の記事は基本中の基本なので、次回はモックやデータプロバイダー等の記事も記載していきます。