2. アノテーション

アノテーションとはメタデータを表す特別な構文のことで、 プログラミング言語のソースコードに追加することができます。 PHP そのものにはソースコードにアノテーションする専用の仕組みはありませんが、 ドキュメンテーションブロックに @アノテーション名 引数 のようなタグを書くことでアノテーションを表すという記法が PHP コミュニティ内で一般に使われています。 PHP では、リフレクション API の getDocComment() メソッドを使えば関数、クラス、メソッド、属性 それぞれのドキュメンテーションブロックにアクセスすることができます。 PHPUnit などのアプリケーションでは、 この情報をもとに実行時の振る舞いを設定するのです。

Note

PHP の doc コメントは、/** で始めて */ で終わる必要があります。 その他の形式のコメントで書いたアノテーションは、無視されます。

本章では、PHPUnit がサポートするすべてのアノテーションについて解説します。

@author

@author アノテーションは @group アノテーション (@group を参照ください) のエイリアスで、 テストの作者にもとづいたフィルタリングができるようになります。

@after

@after アノテーションを使うと、 テストケースクラス内の各テストメソッドを実行した後に呼ぶメソッドを指定できます。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

final class MyTest extends TestCase
{
    /**
     * @after
     */
    public function tearDownSomeFixtures(): void
    {
        // ...
    }

    /**
     * @after
     */
    public function tearDownSomeOtherFixtures(): void
    {
        // ...
    }
}

@afterClass

@afterClass アノテーションを使うと、 テストケースクラス内の各テストメソッドを実行した後に呼ぶ静的メソッドを指定できます。 ここで共有フィクスチャの後始末をします。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

final class MyTest extends TestCase
{
    /**
     * @afterClass
     */
    public static function tearDownSomeSharedFixtures(): void
    {
        // ...
    }

    /**
     * @afterClass
     */
    public static function tearDownSomeOtherSharedFixtures(): void
    {
        // ...
    }
}

@backupGlobals

PHPUnit では、グローバル変数やスーパーグローバル変数の値をバックアップしてからテストを始め、 テストが終わった後でそれらの値を復元することができます。

@backupGlobals enabled アノテーションはクラスレベルで使うことができます。 この場合、テストケースクラスのすべてのテストでグローバル変数の保存と復元が行われます。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

/**
 * @backupGlobals enabled
 */
final class MyTest extends TestCase
{
    // ...
}

@backupGlobals アノテーションをテストメソッドレベルで使うこともできます。 これによって、保存と復元の操作をより細やかに制御できるようになります。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

/**
 * @backupGlobals enabled
 */
final class MyTest extends TestCase
{
    public function testThatInteractsWithGlobalVariables()
    {
        // ...
    }

    /**
     * @backupGlobals disabled
     */
    public function testThatDoesNotInteractWithGlobalVariables(): void
    {
        // ...
    }
}

@backupStaticAttributes

PHPUnit では、宣言されたクラス内のすべての static 属性の値をバックアップしてからテストを始め、 テストが終わった後でそれらの値を復元することができます。

@backupStaticAttributes enabled アノテーションはクラスレベルで使うことができます。 この場合、テストケースクラスのすべてのテストで static 属性の保存と復元が行われます。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
 * @backupStaticAttributes enabled
 */
final class MyTest extends TestCase
{
    // ...
}

@backupStaticAttributes アノテーションをテストメソッドレベルで使うこともできます。 これによって、保存と復元の操作をより細やかに制御できるようになります。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

/**
 * @backupStaticAttributes enabled
 */
final class MyTest extends TestCase
{
    public function testThatInteractsWithStaticAttributes(): void
    {
        // ...
    }

    /**
     * @backupStaticAttributes disabled
     */
    public function testThatDoesNotInteractWithStaticAttributes(): void
    {
        // ...
    }
}

Note

PHP の内部的な制約のため、 @backupStaticAttributes が、 意図していない static 値を保存してしまい、 その後のテストに影響してしまうことがあります。

詳細は グローバルな状態 を参照ください。

@before

@before アノテーションを使うと、 テストケースクラス内の各テストメソッドを実行する前に呼ぶメソッドを指定できます。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

final class MyTest extends TestCase
{
    /**
     * @before
     */
    public function setupSomeFixtures(): void
    {
        // ...
    }

    /**
     * @before
     */
    public function setupSomeOtherFixtures(): void
    {
        // ...
    }
}

@beforeClass

@beforeClass アノテーションを使うと、 テストケースクラス内の各テストメソッドを実行する前に呼ぶ静的メソッドを指定できます。 ここで共有フィクスチャの準備をします。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

final class MyTest extends TestCase
{
    /**
     * @beforeClass
     */
    public static function setUpSomeSharedFixtures(): void
    {
        // ...
    }

    /**
     * @beforeClass
     */
    public static function setUpSomeOtherSharedFixtures(): void
    {
        // ...
    }
}

@codeCoverageIgnore*

@codeCoverageIgnore@codeCoverageIgnoreStart、そして @codeCoverageIgnoreEnd アノテーションを使うと、 コード内の特定の行をカバレッジ解析の対象外にできます。

利用法は コードブロックの無視 を参照ください。

@covers

@covers アノテーションをテストコードで使うと、 そのテストメソッドがどのメソッドをテストするのかを指定することができます。

/**
 * @covers \BankAccount
 */
public function testBalanceIsInitiallyZero(): void
{
    $this->assertSame(0, $this->ba->getBalance());
}

@covers アノテーションが利用された場合、出力されるコードカバレッジの算出方法が変わります。 アノテーションで自身を指定するテストから実行された場合のみ、「covered」と判定されます。 アノテーションで自身を指定していないテストから間接的に利用されたとしても、「covered」とは判定されません。 これによって、コードカバレッジが実際よりも高く出る false positive を避けることができます。

このアノテーションはテストクラスの docblock に記載することも、個別のテストメソッドの docblock に記載することもできますが、 テストクラスの docblock に記載することが推奨され、個別のテストメソッドの docblock に記載することは推奨されません。

XML 設定ファイルforceCoversAnnotation オプションを true に設定した場合、 全てのテストメソッドにおいて covers アノテーションを設定しなければなりません (テストクラス単位で設定しても、それぞれのテストメソッドを個別に設定しても構いません)

Table 2.2@covers アノテーションの構文を示します。

対象とするコードパーツの指定 のセクションでは、このアノテーションを使うより詳細な例を紹介しています。

このアノテーションは、完全修飾クラス名を必要とすることに注意してください。 完全修飾クラス名の先頭のバックスラッシュは省略しても動作に問題ありませんが、読み手に完全修飾クラス名であることを伝えるために 先頭にバックスラッシュをつける形式で記載することが推奨されます。

Table 2.2 カバーするメソッドを指定するためのアノテーション
アノテーション 説明
@covers ClassName::methodName (not recommended) そのテストメソッドが指定したメソッドをカバーすることを表します。
@covers ClassName (recommended) そのテストメソッドが指定したクラスのすべてのメソッドをカバーすることを表します。
@covers ClassName<extended> (not recommended) そのテストメソッドが、指定したクラスとその親クラスおよびインターフェイスのすべてのメソッドをカバーすることを表します。
@covers ClassName::<public> (not recommended) そのテストメソッドが、指定したクラスのすべての public メソッドをカバーすることを表します。
@covers ClassName::<protected> (not recommended) そのテストメソッドが、指定したクラスのすべての protected メソッドをカバーすることを表します。
@covers ClassName::<private> (not recommended) そのテストメソッドが、指定したクラスのすべての private メソッドをカバーすることを表します。
@covers ClassName::<!public> (not recommended) そのテストメソッドが、指定したクラスのすべての非 public メソッドをカバーすることを表します。
@covers ClassName::<!protected> (not recommended) そのテストメソッドが、指定したクラスのすべての非 protected メソッドをカバーすることを表します。
@covers ClassName::<!private> (not recommended) そのテストメソッドが、指定したクラスのすべての非 private メソッドをカバーすることを表します。
@covers ::functionName (recommended) そのテストメソッドが、指定したグローバル関数をカバーすることを表します。

@coversDefaultClass

@coversDefaultClass アノテーションを使うと、 デフォルトの名前空間あるいはクラス名を指定できます。 こうすることで、 @covers アノテーションのたびに長い名前を繰り返す必要がなくなります。 Example 2.18 を参照ください。

このアノテーションは、完全修飾クラス名を必要とすることに注意してください。 完全修飾クラス名の先頭のバックスラッシュは省略しても動作に問題ありませんが、読み手に完全修飾クラス名であることを伝えるために 先頭にバックスラッシュをつける形式で記載することが推奨されます。

Example 2.18 @coversDefaultClass を使ったアノテーションの短縮
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

/**
 * @coversDefaultClass \Foo\CoveredClass
 */
final class CoversDefaultClassTest extends TestCase
{
    /**
     * @covers ::publicMethod
     */
    public function testSomething(): void
    {
        $o = new Foo\CoveredClass;
        $o->publicMethod();
    }
}

@coversNothing

@coversNothing アノテーションをテストコードで使うと、 そのテストケースについてはコードカバレッジ情報を記録しないように指定できます。

これはインテグレーションテストで使えます。例として どのメソッドもカバーすべきでないことを指定したテスト を参照ください。

このメソッドはクラスレベルおよびメソッドレベルで使え、 あらゆる @covers タグを上書きします。

@dataProvider

テストメソッドには任意の引数を渡すことができます。 引数は、データプロバイダメソッド (配列の配列を返すデータプロバイダの使用provider()) から渡されます。 使用するデータプロバイダメソッドを指定するには @dataProvider アノテーションを使います。

詳細は データプロバイダ を参照ください。

@depends

PHPUnit は、テストメソッド間の依存性の明示的な宣言をサポートしています。 この依存性とは、テストメソッドが実行される順序を定義するものではありません。 プロデューサーがテストフィクスチャを作ってそのインスタンスを返し、 依存するコンシューマーがそれを受け取って利用するというものです。 @depends アノテーションを使った依存性の表現 は、@depends アノテーションを使ってテストメソッドの依存性をあらわす例です。

詳細は テストの依存性 を参照ください。

@doesNotPerformAssertions

アサーションがひとつもないテストを、リスキーであるとみなさないようにします。

@group

あるテストを、ひとつあるいは複数のグループに属するものとすることができます。 @group アノテーションをこのように使用します。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

final class MyTest extends TestCase
{
    /**
     * @group specification
     */
    public function testSomething(): void
    {
    }

    /**
     * @group regression
     * @group bug2204
     */
    public function testSomethingElse(): void
    {
    }
}

@group アノテーションはテストクラスにも指定できます。 指定すると、そのテストクラスのすべてのメソッドにアノテーションが「継承」されます。

特定のグループに属するテストのみを選んで実行するには、 コマンドラインのテストランナーの場合は --group オプションあるいは --exclude-group オプションを指定します。XML 設定ファイルの場合は、 それぞれ対応するディレクティブを指定します。

@large

@large アノテーションは、 @group large のエイリアスです。

PHP_Invoker パッケージがインストールされていて strict モードが有効な場合に、 large テストは実行時間が 60 秒を超えたら失敗します。 このタイムアウト時間は、XML 設定ファイルの timeoutForLargeTests 属性で変更できます。

@medium

@medium アノテーションは @group medium のエイリアスです。 medium テストは、@large とマークしたテストに依存してはいけません。

PHP_Invoker パッケージがインストールされていて strict モードが有効な場合に、 medium テストは実行時間が 10 秒を超えたら失敗します。 このタイムアウト時間は、XML 設定ファイルの timeoutForMediumTests 属性で変更できます。

@preserveGlobalState

テストを別プロセスで実行するときに、 PHPUnit は親プロセスのグローバルな状態を保存しようと試みます。 親プロセスのすべてのグローバル状態をシリアライズし、 子プロセス内で最後にそれをアンシリアライズするのです。 しかし、親プロセスのグローバル状態の中にもしシリアライズできないものがあれば、 問題が発生します。この問題に対応するために、グローバル状態の保存を無効にすることができます。 そのために使うのが @preserveGlobalState アノテーションです。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

final class MyTest extends TestCase
{
    /**
     * @runInSeparateProcess
     * @preserveGlobalState disabled
     */
    public function testInSeparateProcess(): void
    {
        // ...
    }
}

@requires

@requires アノテーションを使うと、共通の事前条件 (たとえば PHP のバージョンや拡張モジュールのインストール状況) を満たさないときにテストをスキップできます。

条件に指定できる内容やその例については @requires の例用例 を参照ください。

@runTestsInSeparateProcesses

テストクラス内のすべてのテストケースを、個別の PHP プロセスで実行するように指示します。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

/**
 * @runTestsInSeparateProcesses
 */
final class MyTest extends TestCase
{
    // ...
}

注意: デフォルトで、PHPUnit は親プロセスのグローバルな状態を保存しようと試みます。 親プロセスのすべてのグローバル状態をシリアライズし、 子プロセス内で最後にそれをアンシリアライズするのです。 しかし、親プロセスのグローバル状態の中にもしシリアライズできないものがあれば、 問題が発生します。この問題に対応するために、グローバル状態の保存を無効にすることができます。 この問題の対処法については @preserveGlobalState を参照ください。

@runInSeparateProcess

そのテストを個別の PHP プロセスで実行するように指示します。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

final class MyTest extends TestCase
{
    /**
     * @runInSeparateProcess
     */
    public function testInSeparateProcess(): void
    {
        // ...
    }
}

注意: デフォルトで、PHPUnit は親プロセスのグローバルな状態を保存しようと試みます。 親プロセスのすべてのグローバル状態をシリアライズし、 子プロセス内で最後にそれをアンシリアライズするのです。 しかし、親プロセスのグローバル状態の中にもしシリアライズできないものがあれば、 問題が発生します。この問題に対応するために、グローバル状態の保存を無効にすることができます。 この問題の対処法については @preserveGlobalState を参照ください。

@small

@small アノテーションは @group small のエイリアスです。 small テストは、@medium@large とマークしたテストに依存してはいけません。

PHP_Invoker パッケージがインストールされていて strict モードが有効な場合に、 small テストは実行時間が 1 秒を超えたら失敗します。 このタイムアウト時間は、XML 設定ファイルの timeoutForSmallTests 属性で変更できます。

Note

テストの実行時間の制限を有効にするには、@small@medium@large のいずれかのアノテーションで明示的に指定する必要があります。

@test

テストメソッド名の先頭に test をつけるかわりに、メソッドのドキュメンテーションブロックで @test アノテーションを使ってそのメソッドがテストメソッドであることを指定することができます。

/**
 * @test
 */
public function initialBalanceShouldBe0(): void
{
    $this->assertSame(0, $this->ba->getBalance());
}

@testdox

アジャイルドキュメントを生成する際に使う別の説明を指定します。

@testdox アノテーションは、クラスにもテストメソッドにも指定できます。

<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

/**
 * @testdox A bank account
 */
final class BankAccountTest extends TestCase
{
    /**
     * @testdox has an initial balance of zero
     */
    public function balanceIsInitiallyZero(): void
    {
        $this->assertSame(0, $this->ba->getBalance());
    }
}

Note

PHPUnit 7.0 より前のバージョンでは、アノテーションのパースにバグがあるため、 @testdox アノテーションを指定すると自動的に @test アノテーションも指定したものとみなされます。

メソッドレベルで @testdox アノテーションを @dataProvider アノテーションと一緒に利用する場合、別の説明の中でメソッドの引数をプレースホルダとして使えます。

@testWith

テストメソッド @dataProvider とともに使うメソッドを実装するかわりに、 @testWith アノテーションを使ってデータセットを定義することができます。

データセットには複数の要素を含めることができます。 複数の要素からなるデータセットを定義するには、要素ごとに別の行で定義します。 データセットの要素は、JSONの配列形式でなければいけません。

データセットをテストに渡す方法について、詳しくは データプロバイダ を参照ください。

/**
 * @testWith ["test", 4]
 *           ["longer-string", 13]
 */
public function testStringLength(string $input, int $expectedLength): void
{
    $this->assertSame($expectedLength, strlen($input));
}

JSONのオブジェクト形式で書いた場合は、連想配列として扱われます。

/**
 * @testWith [{"day": "monday", "conditions": "sunny"}, ["day", "conditions"]]
 */
public function testArrayKeys(array $array, array $keys): void
{
    $this->assertSame($keys, array_keys($array));
}

@ticket

@ticket アノテーションは @group アノテーション (@group を参照ください) のエイリアスで、 チケットIDにもとづいたテストのフィルタリングができるようになります。

@uses

@uses アノテーションは、 テストから実行されてはいるが、そのテストでカバーするつもりはないコードを指定します。 たとえば、コード片をテストするために必要な値オブジェクトなどに使います。

/**
 * @covers \BankAccount
 * @uses   \Money
 */
public function testMoneyCanBeDepositedInAccount(): void
{
    // ...
}

Example 9.2 にて、他の使用例を確認することができます。

このアノテーションはコードを読む人の助けになることに加え、 意図せずコードをカバーしていた場合にテストを失敗させる厳密なカバレッジモードで使うと特に有用です。 厳密なカバレッジモードに関する詳細な情報は ref:risky-tests.unintentionally-covered-code を参照してください。

このアノテーションは、完全修飾クラス名を必要とすることに注意してください。 完全修飾クラス名の先頭のバックスラッシュは省略しても動作に問題ありませんが、読み手に完全修飾クラス名であることを伝えるために 先頭にバックスラッシュをつける形式で記載することが推奨されます。