はじめに
「テストコードって聞いたことはあるけど、実際どう書けばいいのかわからない」
「PHPUnitって名前は知ってるけど、なんとなく難しそうで避けてきた」
そんな方に向けて、この記事では PHPのテストコードとは何か から始まり、PHPUnitのインストール〜設定〜実際にテストを書いて実行する ところまで、実際に手を動かしながら理解できるように解説します。
この記事を読み終える頃には、
- テストコードが何のためにあるのか
- PHPUnitの環境構築
- テストコードの書き方
- 「このテストは大丈夫」と判断する方法(グリーン/レッド、カバレッジ)
がイメージしていただけたら幸いです。
1. テストコードとは何か
1-1. そもそも「テスト」とは
プログラムを書いていると、必ず「このコード、ちゃんと動くんだっけ?」と不安になる瞬間があります。
通常、動作確認は以下のような方法で行われがちです。
- ブラウザでページを開いて目視確認する
-
var_dump()やechoで値を出力して確認する - 実際にAPIを叩いてPostmanで確認する
これらは手動テストと呼ばれます。手軽ですが、以下のような問題があります。
- 毎回同じ手順を人間がやる必要があり、時間がかかる
- 確認を忘れる、確認漏れが発生する
- 修正のたびに「ここも壊れてないか」を全部人力で確認するのは非現実的
1-2. テストコードとは
テストコードとは、「このコードは期待通りに動くか」をプログラム自身に確認させるためのコードです。
例えば、「2つの数字を足し算する関数」があったとします。
function add(int $a, int $b): int
{
return $a + $b;
}
このとき、「add(1, 2) を実行したら 3 が返ってくるはず」という期待値を、コードとして書いておきます。
// テストコードのイメージ
assert(add(1, 2) === 3);
これを実行するだけで、「本当に3が返ってきているか」を一瞬で確認できます。人間がブラウザを開いて確かめる必要はありません。
1-3. テストコードのメリット
| メリット | 説明 |
|---|---|
| 高速に動作確認できる | コマンド一発で全機能をチェックできる |
| 修正時の安心材料になる | 修正後にテストを流すだけで「壊れていないか」がわかる |
| 仕様書代わりになる | テストを見れば「この関数は何をすべきか」が読み取れる |
| バグの早期発見 | 本番リリース前に問題に気づける |
このテストコードを効率的に書いて実行するための「道具(フレームワーク)」が、PHPの世界では PHPUnit です。
2. テストの種類(ざっくりでOK)
いきなり全部覚える必要はありませんが、言葉だけ知っておくと理解がスムーズです。
- 単体テスト(Unit Test):関数やクラス単体が正しく動くかを確認するテスト。この記事で扱うのはこれです。
- 結合テスト(Integration Test):複数のクラスやDBなど、組み合わせた際の動作を確認するテスト。
- E2Eテスト(End to End Test):ブラウザ操作など、実際のユーザー操作に近い形で確認するテスト。
PHPUnitは主に単体テストのためのツールですが、工夫次第で結合テストにも使えます。まずは単体テストから始めましょう。
3. PHPUnitとは
PHPUnit は、PHPで最も広く使われているテスティングフレームワークです。
- テストコードを「クラス」と「メソッド」という形で整理して書ける
- コマンド一発で全テストを実行できる
- 「成功(グリーン)」「失敗(レッド)」を明確に表示してくれる
- Composerで簡単に導入できる
多くのPHPフレームワーク(Laravel、Symfonyなど)も標準でPHPUnitを利用しています。
4. 環境構築
4-1. 前提条件
以下がインストールされている必要があります。
- PHP(7.3以上推奨、できれば8.x系)
- Composer(PHPのパッケージ管理ツール)
バージョン確認は以下のコマンドで行えます。
php -v
composer -v
もしComposerが入っていない場合は、公式サイトからインストールしてください。
4-2. プロジェクトの作成
作業用フォルダを作ります。
mkdir php-testing-sample
cd php-testing-sample
composer init -n
composer init -n を実行すると composer.json が自動生成されます。
4-3. PHPUnitのインストール
以下のコマンドでPHPUnitを開発用パッケージとしてインストールします。
composer require --dev phpunit/phpunit
インストールが完了すると、vendor/bin/phpunit というコマンドが使えるようになります。動作確認をしてみましょう。
./vendor/bin/phpunit --version
バージョン情報が表示されればインストール成功です。
4-4. ディレクトリ構成
以下のような構成にします。
php-testing-sample/
├── composer.json
├── src/
│ └── Calculator.php // 本体のコード
├── tests/
│ └── CalculatorTest.php // テストコード
├── vendor/
└── phpunit.xml // PHPUnitの設定ファイル
src フォルダにアプリ本体のコードを、tests フォルダにテストコードを置くのが一般的な慣習です。
4-5. オートロードの設定
composer.json を開いて、autoload の設定を追加します(Composerで名前空間からファイルを自動で読み込めるようにする設定です)。
{
"require-dev": {
"phpunit/phpunit": "^10.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
}
}
設定を反映させるために、以下のコマンドを実行します。
composer dump-autoload
5. 実際にテストコードを書いてみる
5-1. テスト対象のコードを作る
まずはテストされる側、「電卓クラス」を作ります。
src/Calculator.php
<?php
namespace App;
class Calculator
{
public function add(int $a, int $b): int
{
return $a + $b;
}
public function subtract(int $a, int $b): int
{
return $a - $b;
}
public function divide(int $a, int $b): float
{
if ($b === 0) {
throw new \InvalidArgumentException('0で割ることはできません');
}
return $a / $b;
}
}
5-2. テストコードを書く
tests/CalculatorTest.php
<?php
namespace Tests;
use App\Calculator;
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
private Calculator $calculator;
// 各テストの実行前に自動で呼ばれる
protected function setUp(): void
{
$this->calculator = new Calculator();
}
public function test_add_returns_correct_sum(): void
{
$result = $this->calculator->add(1, 2);
$this->assertSame(3, $result);
}
public function test_subtract_returns_correct_difference(): void
{
$result = $this->calculator->subtract(5, 3);
$this->assertSame(2, $result);
}
public function test_divide_returns_correct_quotient(): void
{
$result = $this->calculator->divide(10, 2);
$this->assertSame(5.0, $result);
}
public function test_divide_by_zero_throws_exception(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->calculator->divide(10, 0);
}
}
5-3. コードの解説
-
extends TestCase:PHPUnitのテストクラスは必ずTestCaseを継承します。 -
test_〇〇から始まるメソッド名(または@testアノテーション付き):これが「1つのテスト」として認識されます。 -
setUp():各テストの実行前に毎回呼ばれる初期化処理です。 -
assertSame($期待値, $実際の値):「期待した値と実際の値が一致しているか」を確認します。 -
expectException():「この処理は例外を投げるはずだ」という期待を書けます。
6. phpunit.xml の設定
毎回コマンドで細かいオプションを指定するのは面倒なので、設定ファイルを用意します。
プロジェクト直下に phpunit.xml を作成します。
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="Unit Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory>src</directory>
</include>
</source>
</phpunit>
-
bootstrap:Composerのオートロードファイルを読み込む設定です。これがないとApp\Calculatorなどが見つからずエラーになります。 -
testsuite:どのフォルダのテストを実行するかを指定します。 -
colors="true":ターミナルの結果表示をカラフルにしてくれます(見やすくなります)。 -
source > include:カバレッジ計測(後述)の対象フォルダを指定します。
この設定ファイルがあることで、以降は単純に以下のコマンドだけでテストが実行できます。
./vendor/bin/phpunit
7. テストを実行してみる
準備ができたら、実際にテストを実行してみましょう。
./vendor/bin/phpunit
すべて成功した場合、以下のような表示になります。
PHPUnit 10.x by Sebastian Bergmann and contributors.
.... 4 / 4 (100%)
Time: 00:00.010, Memory: 6.00 MB
OK (4 tests, 4 assertions)
-
.(ドット)1つが1つのテストの成功を表します。 - 最後に
OK (4 tests, 4 assertions)と表示されれば、すべてのテストが成功(グリーン) した状態です。
7-1. わざと失敗させてみる
「テストが失敗した場合どう見えるか」も体感しておくと安心です。試しに Calculator.php の add メソッドを壊してみましょう。
public function add(int $a, int $b): int
{
return $a + $b + 1; // わざとバグを入れる
}
この状態でテストを実行すると、以下のような表示になります。
PHPUnit 10.x by Sebastian Bergmann and contributors.
F... 4 / 4 (100%)
There was 1 failure:
1) Tests\CalculatorTest::test_add_returns_correct_sum
Failed asserting that 4 is identical to 3.
/path/to/tests/CalculatorTest.php:20
FAILURES!
Tests: 4, Assertions: 4, Failures: 1.
-
Fは失敗(Failure)を表します。 - どのテストが、どこで、何を期待していて、実際は何だったのかが明確に表示されます。
これが「テストコードがあると何が嬉しいのか」を体感できる瞬間です。バグを入れた瞬間に、どこで何が壊れたのかが一目瞭然になります。修正が終わったら忘れずに元に戻しておきましょう。
8. よく使うアサーション一覧
「アサーション」とは「〇〇のはずだ、と主張する(確認する)メソッド」のことです。代表的なものをまとめます。
| メソッド | 意味 |
|---|---|
assertSame($expected, $actual) |
値と型が完全に一致するか(===と同等) |
assertEquals($expected, $actual) |
値が等しいか(型は緩め、==と同等) |
assertTrue($condition) |
条件がtrueか |
assertFalse($condition) |
条件がfalseか |
assertNull($value) |
値がnullか |
assertCount($count, $array) |
配列の要素数が一致するか |
assertInstanceOf(Class::class, $object) |
指定クラスのインスタンスか |
expectException(Exception::class) |
指定の例外が発生するか |
assertStringContainsString($needle, $haystack) |
文字列に部分文字列が含まれるか |
基本的には assertSame を使う場面が最も多いです。「型まで含めて厳密に一致しているか」を確認できるためです。
9. 「このテストは問題ない」とどうやって判断するのか
ここが今回の記事で一番大事なポイントです。テストコードを書いただけでは意味がなく、「テストが正しく機能しているか」を判断する視点が必要です。
9-1. まずは「グリーンになっているか」
一番シンプルな基準は、OK (n tests, n assertions) という**グリーン(全成功)**の表示が出ているかどうかです。1つでも F(失敗)や E(エラー)が出ていたら、問題ありです。
9-2. わざと壊してみて「ちゃんと落ちるか」を確認する
意外と見落とされがちですが、「テストが通ること」だけでなく「バグを入れたらちゃんとテストが失敗すること」も確認すべきです。
なぜなら、書き方によっては「何をテストしても必ず成功してしまう、意味のないテスト」になってしまうことがあるからです(例:assertTrue(true) だけ書いてしまっているケースなど)。
そのため、テストコードを書いたら一度、本体のコードにわざとバグを入れてみて、**ちゃんとテストが赤くなる(失敗する)**ことを確認する習慣をつけましょう。これを俗に「テストのテスト」と呼んだりします。
9-3. カバレッジ(Coverage)を確認する
カバレッジとは、「本体のコードのうち、どれくらいの割合がテストによって実行されたか」を示す指標です。
カバレッジを計測するには Xdebug または PCOV という拡張機能が必要です。以下のコマンドでインストール状況を確認できます。
php -m | grep -i xdebug
入っていない場合はインストールが必要ですが(環境によって手順が異なるため割愛します)、入っていれば以下のコマンドでカバレッジレポートを取得できます。
./vendor/bin/phpunit --coverage-text
実行結果イメージ:
Code Coverage Report:
Summary:
Classes: 100.00% (1/1)
Methods: 100.00% (3/3)
Lines: 90.00% (9/10)
- Classes/Methods/Lines のカバレッジ率が表示されます。
- 100%が必須というわけではありませんが、重要なロジック(お金の計算、認証処理など)は高いカバレッジを目指すべきです。
- カバレッジが低い=「テストされていないコードが多い」=「バグが潜んでいる可能性が高い」という目安になります。
HTML形式で見やすくレポート化することもできます。
./vendor/bin/phpunit --coverage-html coverage-report
実行後、coverage-report/index.html をブラウザで開くと、どの行がテストされていないかが色分けされて表示されます。
9-4. CI(継続的インテグレーション)に組み込む
チーム開発では、コードをpushするたびに自動でテストが実行される仕組み(CI)を用意するのが一般的です。GitHub Actionsを使う場合の例を載せておきます。
.github/workflows/phpunit.yml
name: PHPUnit Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- name: Install dependencies
run: composer install --prefer-dist --no-progress
- name: Run tests
run: ./vendor/bin/phpunit
これにより、人間が忘れずにテストを実行する」のではなく「機械的に必ずテストが実行される状態になります。これが「テストコードが問題ないとわかる」ための、最も確実な仕組みです。
10. まとめ
| 段階 | やること |
|---|---|
| ① 理解 | テストコードとは「期待通り動くかをプログラムに確認させる仕組み」 |
| ② 導入 |
composer require --dev phpunit/phpunit でインストール |
| ③ 設定 |
phpunit.xml を用意し、tests フォルダを対象に指定 |
| ④ 実装 |
TestCase を継承し、assertSame などでテストを書く |
| ⑤ 実行 |
./vendor/bin/phpunit でグリーンになるか確認 |
| ⑥ 検証 | わざとバグを入れて赤くなるか確認/カバレッジを計測 |
| ⑦ 運用 | CIに組み込み、自動でテストが走る仕組みを作る |
テストコードは「書いたら終わり」ではなく、「そのテストが本当に意味のあるチェックになっているか」を継続的に見直すことが大切です。今回紹介した「わざと壊してみる」「カバレッジを見る」「CIに乗せる」を実践すれば、初心者の方でも十分に「安心できるテストコード」を書けるようになります。
ぜひ手元の環境で、実際にコマンドを打ちながら試してみてください。
参考リンク
JISOUのメンバー募集中!
プログラミングコーチングJISOUでは、新たなメンバーを募集しています。日本一のアウトプットコミュニティでキャリアアップしませんか?
興味のある方は、ぜひホームページをのぞいてみてください!