PHPUnit: TestCaseのプロパティのリセットについては神経質にならなくても良い


PHPUnitではPHPUnit\Framework\TestCaseクラスを継承してテストクラスを作るわけだが、そのテストクラスのプロパティについて次のような疑問があった。

  • テストクラスのプロパティは、テストメソッドをまたいで共有されるのか?

どういうことかというと、例えば$countというインスタンスプロパティをテストクラスが持っているとする。その初期値は0とする。1つ目のテストメソッドで$count1に値を変更する。そうしたとき、その後に実行されるテストメソッドでは、$countが初期値の0に戻っているのか、それとも1つ目のテストメソッドの変更をうけて1になっているのか。どちらの振る舞いになるかが疑問になるところだ。

final class PropertiesLifeCycleTest extends TestCase
{
    private $count = 0;

    public function test1(): void
    {
        $this->count = 1;
    }

    public function test2(): void
    {
        // ここで $this->count の値は 0 なのか? それとも 1 なのか?
    }
}

予想される振る舞いの仮説

共有仮説: インスタンスプロパティはテストメソッドで共有される
独立仮説: テストメソッドは独立しており、インスタンスプロパティはテストメソッド実行前に必ず初期化される

もし、共有仮説が正しいとすると、インスタンスプロパティをリセットすることに神経を使わなければならない。例えば、setUpで初期化するなど:

final class PropertiesLifeCycleTest extends TestCase
{
    private $count;

    protected function setUp()
    {
        $this->count = 0; // 初期化
    }

    public function test1(): void
    {
        $this->count = 1;
    }

    public function test2(): void
    {
        // ここで $this->count の値は 0 なのか? それとも 1 なのか?
    }
}

一方の独立仮説が正しい場合、インスタンスプロパティをリセットする処理を自分で書かなくて済む。

仮説を検証してみた

次のようなテストクラスを実装し、テストを実行して「独立仮説」が正しいことを検証してみた。

final class PropertiesLifeCycleTest extends TestCase
{
    private const INITIALIZED = 1;
    private const MUTATED = 2;

    private $property = self::INITIALIZED;

    public function test1(): void
    {
        self::assertSame(self::INITIALIZED, $this->property, 'プロパティは初期状態であること');
        $this->property = self::MUTATED;
        self::assertSame(self::MUTATED, $this->property, 'テストメソッドはプロパティを変更していること');
    }

    public function test2(): void
    {
        self::assertSame(self::INITIALIZED, $this->property, 'プロパティは初期状態であること');
        $this->property = self::MUTATED;
        self::assertSame(self::MUTATED, $this->property, 'テストメソッドはプロパティを変更していること');
    }
}

より詳しい検証コードはGitHubに公開している。詳細な検証コードではData Providerを使ったパターンや、setUptearDownを組み合わせたときの検証も行った。

検証結果

このテストはFAILにならなかった。したがって、独立仮説が正しいことになる。

結論

テストメソッドは独立しており、インスタンスプロパティはテストメソッド実行前に必ず初期化される。よって、TestCaseのプロパティのリセットについては神経質にならなくても良い。