[PHPUnit]attributesを使ってテストデータを効率的に管理する
はじめに
こんにちは、booost technologiesバックエンドエンジニアのma_meです。
チーム内でユニットテストの熱が高まる中、PHPUnitの知識が数年前で止まっていたので、がんがんアップデート中です。
PHPにattributesが導入されてから、PHPUnitも追従してattributesが使えるようになっていました。
従来アノテーションを使って記法していたときは、静的解析が効きづらい、補完が効かないといった課題がありましたが、PHPUnitのattributesではこれらの問題が解消されています。
今回は特に便利だと感じた、PHPUnitのattributesを使ってデータセットを用意する方法と、アノテーションとの比較点をまとめました。
TestWith Attributes
TestWith Attributesの特徴は以下の通りです。
- メソッド定義不要
- クラス定義不要
- テストデータをテストメソッドのすぐ上に記述できる
感覚的にJestのeachのように書けるので、親しみやすいと感じました。
書式
#[TestWith([データセット])]
public function テストメソッド名(): void
準備
各Attributeをuseする
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\TestWith;
サンプルコード
<?php
namespace Tests;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\TestWith;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
#[Test]
#[TestWith([0, 0, 0])]
#[TestWith([0, 1, 1])]
public function 数字加算(int $a, int $b, int $expected): void
{
$this->assertSame($expected, $a + $b);
}
#[Test]
#[TestWith(['booost', 'technologies', 'booost technologies'])]
public function 文字連結(string $a, string $b, string $expected): void
{
$this->assertSame($expected, $a . ' ' . $b);
}
}
@testWith アノテーションとの違い
annotationのケースでは下記のように書きました。
/**
* @testWith ["test", 4]
* ["longer-string", 13]
*/
以前はこのような問題を抱えていました。
- コメント扱いなので、補完が効きづらい
- 静的解析が効きづらい
- 文字列は必ずダブルクォートで記載する必要がある
これらの要素がTestWith Attributesで解消されています。
DataProviderExternal Attributes
DataProviderExternal Attributesの特徴は以下の通りです。
- メソッド定義とクラス定義が必要
- クラス定義とメソッド定義を個別に指定して使える
- テストコードとデータセットを分離できる
データセットを使いまわしたい場合や、テストコードにデータセットを記述したくない場合にはとても便利です。
書式
#[DataProviderExternal(データセットのクラス名, '呼び出したいメソッド名')]
public function テストメソッド名(): void
準備
各Attributeとデータセットのクラスをuseする
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\DataProviderExternal;
use データセットクラス
サンプルコード
データセットクラス
<?php
namespace Tests\DataProvider;
use PHPUnit\Framework\TestCase;
class SampleTestDataProvider extends TestCase
{
public static function 数字加算DataSet(): array
{
return [
[0, 0, 0],
[0, 1, 1],
];
}
public static function 文字連結DataSet(): array
{
return [
['booost', 'tech', 'booost tech'],
['booost', 'technologies', 'booost technologies']
];
}
}
テストコード
<?php
namespace Tests;
use Tests\DataProvider\SampleTestDataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\DataProviderExternal;
class SampleTest extends TestCase
{
#[Test]
#[DataProviderExternal(SampleTestDataProvider::class, '数字加算DataSet')]
public function 数字加算2(int $a, int $b, int $expected): void
{
$this->assertSame($expected, $a + $b);
}
#[Test]
#[DataProviderExternal(SampleTestDataProvider::class, '文字連結DataSet')]
public function 文字連結2(string $a, string $b, string $expected): void
{
$this->assertSame($expected, $a . ' ' . $b);
}
}
DataProvider アノテーションとの違い
DataProviderExternalアノテーションに該当するものは無く、DataProviderアノテーションがありました。
DataProviderアノテーションでは下記のように書けました。
/**
* @dataProvider データプロパイダーメソッド名
*/
以前はこのような問題を抱えていました。
- データプロバイダーメソッドを同じファイルに記載する必要があった
- データセットの使いまわしが同クラス内でしかできなかった。
この点がDataProviderExternal Attributesでは解消されています。
アノテーションとattributesの比較まとめ
特徴 | アノテーション | attributes |
---|---|---|
静的解析の対応 | 効きづらい | 効く |
テストデータの記述場所 | コメント内 | PHP構文そのもの |
データセットの場所 | 同じファイル内 | 外部クラスから呼び出し可能 |
記述の簡潔さ | やや複雑 | シンプルで読みやすい |
サポートする構文 | PHPDoc形式での記述 | ネイティブなPHP構文 |
この記事が、効率的なテストコード作成の参考になれば幸いです
宣伝
booost technologiesではPHPエンジニアを積極採用中です。
もしご興味をお持ちいただけましたら、ぜひお気軽にお声がけください。
Discussion