PHPUnit11でテストを実行すると、実行後の出力に「PHPUnit Deprecations」というメッセージが表示されるようになっていました。@testなどの慣れ親しんだアノテーションはPHPUnit12からサポート外となるようですので、新しいアトリビュートへ変更します。

PHPUnit12で削除される@アノテーション

OK, but there were issues!
Tests: 28, Assertions: 64, PHPUnit Deprecations: 1.

こちらがテスト時に表示されたメッセージです。「PHPUnit Deprecations: 1」だけだと分かりにくいので、詳細内容も出力されるようにphpunit.xmlを少し修正します。

...
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true"
         displayDetailsOnPhpunitDeprecations="true" //これを追加
>
...

phpunitタグにdisplayDetailsOnPhpunitDeprecations="true"を追加しました。これで再度テストを実行すると、以下のようなメッセージが出力されました。

There was 1 PHPUnit test runner deprecation:

1) Metadata found in doc-comment for method Tests\Unit\Models\UserModelTest::is_admin_check(). Metadata in doc-comments is deprecated and will no longer be supported in PHPUnit 12. Update your test code to use attributes instead.

メッセージによると、ドキュメントコメント内のアノテーションは非推奨とのこと。PHPUnit12ではサポート対象外となるため、テストのアノテーションは「アトリビュート」に書き換える必要があるようです。

アノテーション → アトリビュートの書き換え

今まで使っていたアノテーションの記述は、アトリビュートでは以下のように書き換えます。

  • @test#[Test]
  • @dataProvider#[DataProvider('データプロバイダ名')]
  • @covers#[CoversMethod('カバー対象のクラス', 'カバー対象メソッド')]

他のアトリビュートの記述はドキュメントで紹介されていますのでご参照ください。

基本的にはこれらを書き換えればいいだけなのですが、@coversに関しては記述の位置が大きく変更となるためご注意ください。では具体的にどう変更になったか?というのを、以下のテストコードを使ってご紹介します。

変更前のテスト(アノテーション使用)

こちらがアノテーションを使用したユニットテストです。ドキュメントコメント内に@test@covers@dataProviderを使用しています。

namespace Tests\Unit\Models;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class UserModelTest extends TestCase
{
    use RefreshDatabase;

    /**
     * @test
     * @covers App\Models\User::isAdmin
     * @dataProvider roleDataProvider
     */
    public function is_admin_check(string $role, bool $expected): void
    {
        $user = User::factory()->create(['role' => $role]);

        $this->assertEquals($expected, $user->isAdmin());
    }

    public static function roleDataProvider(): array
    {
        return [
            'role = admin' => [
                'role' => 'admin',
                'expected' => true
            ],
            'role = user' => [
                'role' => 'user',
                'expected' => false
            ],
            'role = role' => [
                'role' => 'editor',
                'expected' => false
            ],
        ];
    }
}

変更後のテスト(アトリビュート使用)

以下が、アトリビュートに書き換えた後のテストになります。

namespace Tests\Unit\Models;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\CoversMethod;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

#[CoversMethod(User::class, 'isAdmin')]
class UserModelTest extends TestCase
{
    use RefreshDatabase;

    #[Test]
    #[DataProvider('roleDataProvider')]
    public function test_is_admin_check(string $role, bool $expected): void
    {
        $user = User::factory()->create(['role' => $role]);

        $this->assertEquals($expected, $user->isAdmin());
    }

    public static function roleDataProvider(): array
    {
        return [
            'role = admin' => [
                'role' => 'admin',
                'expected' => true
            ],
            'role = user' => [
                'role' => 'user',
                'expected' => false
            ],
            'role = role' => [
                'role' => 'editor',
                'expected' => false
            ],
        ];
    }
}

まず、use#[Test]などの各属性に対応するクラスをインポートする必要があります。そして、#[Test]#[DataProvider()]はアノテーションと同様テストケースの直前に配置します。

一番大きな変更である@coversの位置ですが、アトリビュートではクラス宣言の直前に置く形になります。
ドキュメントにもあるように、#[CoversMethod()]は各テストケースではなくテストクラスに指定する必要があります。

最初このルールに気が付かず、@coversと同じように各テストケースの前に#[CoversMethod()]を配置したところ以下のようなエラーが発生しテスト失敗となりました。

$ ./vendor/bin/phpunit

An error occurred inside PHPUnit.

Message:  Attribute "PHPUnit\Framework\Attributes\CoversMethod" cannot target method (allowed targets: class)

ちゃんとエラーが出てくれたので、気がつけてよかったです。

改めてテストを実行

アトリビュートへの書き換えが正しくできているか、再度テストを実行してみます。

$ ./vendor/bin/phpunit

OK (28 tests, 64 assertions)

Deprecationsのメッセージが出なくなりました。テスト数やアサーションの数も同じなので、書き換えは問題なくできたようです。

カバレッジの出力を確認

#[CoversMethod()]の動作確認も兼ねて、カバレッジの出力も見てみます。私の環境ではpcovを使用しています。まだの方はインストールと、php.iniにて以下を設定ください。

extension=/path/to/your/pcov.so
pcov.enabled=1
pcov.directory=/path/to/your/project

では、カバレッジのオプションをつけてテストを実行します。

$ ./vendor/bin/phpunit --coverage-html coverage

実行後、プロジェクトルートのcoverageディレクトリにhtmlファイルが生成され、#[CoversMethod]で指定した関数もちゃんとカバレッジが計測されていました。

メルマガ購読の申し込みはこちらから。

By hmatsu