Skip to content

PHPUnitテスト実行ガイド ​

目的 ​

このドキュメントは、プロジェクトでPHPUnitを使用してテストを実行する方法を初心者向けに説明します。

対象読者 ​

  • テスト経験が少ない開発者
  • このプロジェクトに新しく参加する開発者
  • PHPUnitを初めて使用する開発者

PHPUnitとは ​

PHPUnitの役割と目的 ​

PHPUnitは、PHPアプリケーションのユニットテストを実行するためのテストフレームワークです。このプロジェクトでは、以下の目的で使用しています:

  • コードの品質保証: 実装したコードが期待通りに動作することを確認
  • リファクタリングの安全性: コードを変更しても既存の機能が壊れていないことを確認
  • バグの早期発見: 本番環境にデプロイする前に問題を発見

このプロジェクトでのテストの種類 ​

このプロジェクトでは、以下の2種類のテストを用意しています:

  1. ユニットテスト (tests/Unit/)

    • 個別のクラスやメソッドの動作を検証
    • 現在23個のテストファイルが存在
    • 例: DailyArticleResultServiceTest.php、DailyDataRepositoryBehaviorTest.phpなど
  2. パフォーマンステスト (tests/Performance/)

    • コードの実行速度やメモリ使用量を検証
    • 例: ConverterPerformanceTest.php(シングルトン化によるパフォーマンス向上を検証)

環境セットアップ ​

前提条件 ​

  • PHP: 8.3以上
  • Composer: 依存関係管理ツール

依存関係のインストール方法 ​

プロジェクトルートディレクトリで以下のコマンドを実行してください:

bash
# 開発依存関係を含めてインストール
composer install

# または、setupスクリプトを使用(依存関係インストール + レポートディレクトリ作成)
composer setup

これにより、PHPUnit 11.0以上とその他のテスト関連ツール(Brain Monkey、Mockeryなど)がインストールされます。


テスト実行方法 ​

基本コマンド ​

すべてのテストを実行 ​

bash
composer test

または、PHPUnitを直接実行:

bash
vendor/bin/phpunit

テストカバレッジレポートを生成 ​

bash
composer test-coverage

カバレッジレポートは reports/coverage/ ディレクトリにHTML形式で生成されます。ブラウザで reports/coverage/index.html を開くと、カバレッジレポートを確認できます。

特定のテストファイルを実行 ​

特定のテストファイルのみを実行する場合:

bash
# 特定のテストファイルを実行
vendor/bin/phpunit tests/Unit/Service/DailyArticleResultService/DailyArticleResultServiceTest.php

# または、Composerスクリプト経由で実行
vendor/bin/phpunit -- tests/Unit/Service/DailyArticleResultService/DailyArticleResultServiceTest.php

特定のテストメソッドを実行 ​

特定のテストメソッドのみを実行する場合:

bash
# --filter オプションを使用
vendor/bin/phpunit --filter test_method_name

# 例: DailyArticleResultServiceTestの特定のメソッドを実行
vendor/bin/phpunit --filter DailyArticleResultServiceTest::test_specific_method

フィルタを使った実行 ​

テストクラス名でフィルタ ​

bash
vendor/bin/phpunit --filter DailyArticleResultServiceTest

テストスイートでフィルタ ​

bash
# Unitテストのみ実行
vendor/bin/phpunit tests/Unit

# パフォーマンステストのみ実行
vendor/bin/phpunit tests/Performance

テスト構成の説明 ​

phpunit.xml の設定内容 ​

プロジェクトルートにある phpunit.xml ファイルで、PHPUnitの動作を設定しています。

主要な設定項目 ​

  • bootstrap: tests/bootstrap.php - テスト実行前に読み込まれるファイル
  • colors: true - カラー出力を有効化
  • processIsolation: false - テストを個別のプロセスで実行しない(高速化)
  • stopOnFailure: false - エラーが発生しても全テストを実行
  • cacheDirectory: .phpunit.cache - キャッシュディレクトリ
  • executionOrder: depends,defects - 依存関係と欠陥のあるテストを優先実行

テストスイート ​

xml
<testsuites>
    <testsuite name="Unit">
        <directory>tests/Unit</directory>
    </testsuite>
</testsuites>

現在は Unit テストスイートのみが定義されています。パフォーマンステストは手動で指定して実行します。

ソースコードの指定 ​

xml
<source>
    <include>
        <directory>core_src</directory>
    </include>
</source>

カバレッジレポート生成時に、core_src/ ディレクトリ内のコードを対象とします。

拡張(遅いテストの一覧) ​

xml
<extensions>
    <bootstrap class="Ergebnis\PHPUnit\SlowTestDetector\Extension">
        <parameter name="maximum-count" value="15"/>
        <parameter name="maximum-duration" value="1000"/>
    </bootstrap>
</extensions>

ergebnis/phpunit-slow-test-detector が、1 秒(ミリ秒で指定)を超えたテストを最大 15 件、実行結果の末尾に表示します。表示だけで失敗にはなりません。見方は「テストが遅い」を参照してください。

PHP設定 ​

xml
<php>
    <ini name="memory_limit" value="-1"/>  <!-- メモリ制限なし -->
    <ini name="error_reporting" value="-1"/>  <!-- すべてのエラーを報告 -->
    <ini name="display_errors" value="1"/>  <!-- エラーを表示 -->
</php>

テストディレクトリ構造 ​

tests/
├── bootstrap.php              # テスト実行前の初期化ファイル
├── Unit/                      # ユニットテスト
│   ├── Factory/
│   ├── Model/
│   ├── PostType/
│   ├── Repository/
│   ├── Service/
│   ├── ShortCode/
│   └── Util/
└── Performance/               # パフォーマンステスト
    └── ConverterPerformanceTest.php

bootstrap.php の役割 ​

tests/bootstrap.php は、すべてのテストの実行前に読み込まれるファイルです。以下の処理を行います:

  1. Brain Monkeyの有効化: WordPress関数のモックを有効化
  2. Composerオートローダーの読み込み: アプリケーションクラスとテストツールを読み込み
  3. WordPress関数のモック: wpdb、wp_cache_get、wp_cache_setなどのモック定義
  4. 環境変数の設定: テーマディレクトリパスなどの設定

テスト結果の見方 ​

成功時の出力 ​

テストが成功した場合、以下のような出力が表示されます:

PHPUnit 11.x.x by Sebastian Bergmann and contributors.

Runtime:       PHP 8.3.x
Configuration: /path/to/phpunit.xml

.................                                              17 / 17 (100%)

Time: 00:00.123, Memory: 12.00 MB

OK (17 tests, 45 assertions)
  • OK: すべてのテストが成功
  • 17 tests: 実行されたテストメソッドの数
  • 45 assertions: 実行されたアサーション(検証)の数

失敗時の出力 ​

テストが失敗した場合、以下のような出力が表示されます:

PHPUnit 11.x.x by Sebastian Bergmann and contributors.

Runtime:       PHP 8.3.x
Configuration: /path/to/phpunit.xml

F                                                                   1 / 1 (100%)

Time: 00:00.045, Memory: 8.00 MB

There was 1 failure:

1) Tests\Unit\Service\DailyArticleResultServiceTest::test_something
Failed asserting that false is true.

/path/to/tests/Unit/Service/DailyArticleResultServiceTest.php:42

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
  • F: 失敗したテスト(Failure)
  • Failed asserting that false is true: アサーションの失敗内容
  • /path/to/...: 失敗が発生したファイルと行番号

エラーメッセージの読み方 ​

よくあるエラーメッセージ ​

  1. Failed asserting that...

    • アサーション(検証)が失敗
    • 期待値と実際の値が異なる
  2. Class 'App\SomeClass' not found

    • クラスが見つからない
    • オートローダーの問題、または名前空間の誤り
  3. Call to undefined function...

    • 関数が定義されていない
    • WordPress関数のモックが正しく設定されていない可能性
  4. Undefined property...

    • プロパティが定義されていない
    • モックオブジェクトの設定が不完全

カバレッジレポートの確認方法 ​

HTMLレポートの確認 ​

bash
composer test-coverage

実行後、reports/coverage/index.html をブラウザで開きます。

カバレッジレポートの見方 ​

  • 緑色: テストでカバーされている行
  • 赤色: テストでカバーされていない行
  • 黄色: 部分的にカバーされている行(条件分岐など)

カバレッジレポートでは、以下の情報を確認できます:

  • ファイルごとのカバレッジ率
  • 行ごとの実行回数
  • メソッドごとのカバレッジ率

実際のテストファイル例 ​

ユニットテストの例 ​

プロジェクトには以下のようなユニットテストが含まれています:

  • Service層のテスト: tests/Unit/Service/DailyArticleResultService/DailyArticleResultServiceTest.php
  • Repository層のテスト: tests/Unit/Repository/DailyDataRepositoryBehaviorTest.php
  • Factory層のテスト: (ConverterFactory は削除済み。Factory テストは該当クラスがある場合に追加)
  • Util層のテスト: tests/Unit/Util/HeatMapClassGeneratorTest.php

パフォーマンステストの例 ​

  • Converterのパフォーマンステスト: tests/Performance/ConverterPerformanceTest.php
    • シングルトン化によるパフォーマンス向上を検証
    • 大量のインスタンス作成の実行時間を測定

テストファイルの構造 ​

典型的なテストファイルの構造:

php
<?php
namespace Tests\Unit\Service;

use PHPUnit\Framework\TestCase;
use App\Service\SomeService;

class SomeServiceTest extends TestCase {
    private SomeService $service;

    protected function setUp(): void {
        // テスト実行前の初期化処理
        $this->service = new SomeService();
    }

    public function test_something(): void {
        // テストの実装
        $result = $this->service->doSomething();
        $this->assertTrue($result);
    }
}

トラブルシューティング ​

よくあるエラーと解決方法 ​

1. "Class not found" エラー ​

症状: Class 'App\SomeClass' not found というエラーが発生

原因: オートローダーが正しく読み込まれていない

解決方法:

bash
# ルートおよび core_src で composer install を実行
composer install
cd core_src && composer install

2. WordPress関数のエラー ​

症状: Call to undefined function wp_cache_get() などのエラー

原因: WordPress関数のモックが正しく設定されていない

解決方法: tests/bootstrap.php が正しく読み込まれているか確認。phpunit.xml の bootstrap 設定を確認。

3. メモリ不足エラー ​

症状: Fatal error: Allowed memory size exhausted

解決方法: phpunit.xml でメモリ制限が -1(無制限)に設定されているか確認。それでも問題が発生する場合は、PHPの設定を確認:

bash
php -d memory_limit=-1 vendor/bin/phpunit

4. テストが遅い ​

原因: テストが多すぎる、または個別のテストが重い

解決方法:

  • 特定のテストファイルのみ実行
  • processIsolation が false に設定されているか確認(phpunit.xml)
  • composer test の末尾に出る遅いテストの一覧を見る。phpunit.xml に登録した ergebnis/phpunit-slow-test-detector が、1 秒(maximum-duration)を超えたテストを遅い順に最大 15 件(maximum-count)表示する
  • 一覧は表示だけで、テストは失敗にならない。上位に同じクラスが並ぶときは、大きなファイルの生成や RunInSeparateProcess など、そのテストだけが重い理由がないかを確認する

5. カバレッジレポートが生成されない ​

原因: XdebugまたはPCOVがインストールされていない

解決方法: PHP拡張機能をインストール:

bash
# Xdebugをインストール(例: macOS + Homebrew)
pecl install xdebug

# または、PCOVをインストール
pecl install pcov

インストール後、php.ini に追加:

ini
; Xdebugの場合
zend_extension=xdebug.so

; PCOVの場合
extension=pcov.so

次のステップ ​

関連ドキュメント ​

テストを書く ​

新しい機能を実装する際は、必ずテストも作成してください:

  1. テストファイルを tests/Unit/ の適切なディレクトリに作成
  2. テストクラスは Tests\Unit\ 名前空間を使用
  3. PHPUnit\Framework\TestCase を継承
  4. テストメソッド名は test_ で始める

ベストプラクティス ​

  • 1つのテストメソッドで1つのことをテスト: テストが失敗したときに原因を特定しやすくする
  • 明確なアサーション: 何を検証しているかが分かるようにする
  • モックの適切な使用: 外部依存をモックして、テストを独立させる
  • テストデータの準備: setUp() メソッドで共通の初期化処理を行う

書かないテスト ​

テストの件数やカバレッジを増やすこと自体は目的にしません。次のテストは、壊れたコードを検出できないか、他の仕組みで既に保証されているため書きません(Issue #4007)。

  • クラス・メソッドの存在確認(assertTrue(method_exists(...))、class_exists): 存在しなければ呼び出し側のテストや PHPStan が失敗する。存在するだけでは振る舞いを何も保証しない。tests/Unit の assertTrue(method_exists(...)) / class_exists / interface_exists は composer phpstan-tests(CI Quality Gate の「Run PHPStan (tests)」)でエラーになる
  • インターフェース実装・コンストラクタの戻り値の型だけを確認するテスト(test_implements_interface、test_can_be_instantiated): 型は PHPStan / Deptrac / TypeScript が保証する
  • 定数の値を書き写して比べるだけのテスト(assertSame('foo', Foo::BAR)): 定数を変えるときはテストも同じように書き換えるだけなので、誤りを検出できない。assertSame / assertEquals の一方がリテラル、もう一方が Foo::BAR 形式のクラス定数(self:: / static:: / parent:: と ::class は除く)のものは composer phpstan-tests でエラーになる(Issue #4013)。リテラルとみなすのは、文字列・数値・真偽値と、キーがそれらのリテラル、値がそれらのリテラル・null・入れ子の配列リテラルの配列リテラル([ 'a', 'b' ]、[ 'key' => [ 1, 2 ] ]、[ 'a' => null ]、[]。Issue #4022)。null との比較(配列の要素の null は検出対象)、enum の ->value、グローバル定数、文字列連結、変数・定数・関数呼び出し・展開を含む配列との比較はルールの対象外なので、レビューで見る
    • 例外: 外部との契約になっている値(DB のカラム名・REST のルート・post meta キーなど、変えると本番データや外部の呼び出し元が壊れる値)は書いてよい。その場合は、なぜ値を固定するのかをアサーションの直前の行に日本語のコメントで残す(行コメント・ブロックコメント・docblock のいずれも可。複数行に分けてもよい)。ルールが理由とみなすのは、日本語(ひらがな・カタカナ・漢字)を含み、記号・空白を除いて 10 文字以上のコメント。// Assert や // 検証 のような見出し・短いメモ、空行を挟んだコメント、行末コメント(前の文の $x = 1; // 補足 や、ブロックを開く行の if ( ... ) { // 補足)、phpcs: / @phpstan- などツール向けの指示の行(phpcs:ignore ... -- 補足 の補足も含む)は理由とみなさない。理由コメントのあるアサーションに空行や別の文を挟まず続く同種のアサーションは、先頭のコメントでまとめて許可される。コメントの中身が理由として妥当かは人のレビューで見る

      php
      // 本番 DB の既存固定ページのスラッグ(公開 URL)と一致させる必要がある
      $this->assertSame( 'tokushoho', LegalPagesConstants::TOKUSHOHO_PAGE_SLUG );
      $this->assertSame( 'faq', LegalPagesConstants::FAQ_PAGE_SLUG );
  • モックの戻り値をそのまま assert するだけのテスト: テスト対象のロジックを通らないため、モックの設定を確認しているだけになる
  • 既存テストと同じ経路を別ファイル・別名で重ねるテスト: 失敗したときの修正箇所が増えるだけで、検出できる不具合は増えない。追加する前に、同じクラスの既存の *Test.php / *BehaviorTest.php を確認する
  • カバレッジを埋めるためだけの getter / setter のテスト: 値を入れて取り出すだけでは分岐も変換もないため、回帰を防がない

書くテスト ​

  • 振る舞いを検証するテスト: 分岐・変換・バリデーション・エラー処理など、入力によって結果が変わる部分。コードを壊すと失敗するテストだけが回帰を防ぐ
  • セキュリティのテスト: 権限・nonce・サニタイズ・リダイレクト先の検証。抜けると本番で被害が出るため、正常系だけでなく拒否される経路も書く
  • バグ修正時の回帰テスト: 修正前のコードで失敗することを確かめてから追加する。修正前でも通るテストは、そのバグを防いでいない
  • 似た入力のパターンは DataProvider にまとめる: メソッドを増やさず、入力と期待値の組を並べる。パターンの追加と見比べがしやすくなる

その他 ​

  • RunInSeparateProcess は、定数の定義や require などプロセスの状態を変えるテストだけに使い、理由をコメントで残す: 別プロセスはテストを大きく遅くするため、必要な理由がテストから読めるようにする。#[RunInSeparateProcess](メソッド)/ #[RunTestsInSeparateProcesses](クラス)の docblock か属性の直前(属性の間も可)のコメントに「別プロセス」「別のプロセス」「プロセス分離」「プロセスを分離」「プロセスを分ける(分けて)」のいずれかを含む文がないものは composer phpstan-tests でエラーになる(Issue #4013)。理由は日本語で書く(英語のコメントは理由とみなさない)。テストの説明だけの docblock は理由とみなさない。定数の書き写しの理由コメントと同じく、空行を挟んだコメント(区切りコメントなど)、行末コメント、phpcs: / @phpstan- などツール向けの指示の行に書いた文は理由とみなさない(Issue #4022)。クラスに理由を書いても、メソッドの #[RunInSeparateProcess] にはメソッド側で理由が要る。「以下 N 件も同じ」のようにまとめず、属性ごとに理由を書く

    php
    /**
     * WP_DEBUG を define するため別プロセスで実行する(同一プロセスだと後続テストの WP_DEBUG 分岐が変わる)。
     */
    #[RunInSeparateProcess]
    public function test_xxx(): void {
  • テストを追加したこと自体を成果にしない: PR 説明には、追加したテストがどの回帰(どの変更で壊れたら失敗するか)を防ぐかを書く

Cursor では tests/**/*.php と **/*.test.{ts,tsx,js,mjs}(Vitest のテストと、scripts/ や docs/.vitepress/scripts/ にある node --test 用の *.test.mjs)を編集するときに .cursor/rules/testing-guidelines.mdc が適用されます。Vitest 側の補足は Vitest 実行ガイド を参照してください。


最終更新: 2026-10-07