PHPUnit で例外・トレイトのテスト
PHPUnit の例外・トレイトのテスト方法についてまとめたのでメモ。
環境情報
例外をテストする
例外をスローするメソッドをテストしたい。
<?php class Sample { const ERROR_CODE_1 = 10; public function foo($int) { if ($bool !== 1) { throw new ErrorException('例外のテスト', self::ERROR_CODE_1); } return $int; } }
例外がスローされたかをテストする方法には以下の2つがある。
@expectedException
アノテーションexpectedException()
メソッド
@expectedException
アノテーションは後ろに検査したい例外クラスを指定する。
expectedException()
メソッドは引数に検査したい例外クラスを指定する。
<?php class SampleTest extends TestCase { /** * @expectedException ErrorException */ public function testSample1() { $sample = new Sample(); $sample->foo(true); } public function testSample2() { $this->expectException(ErrorException::class); $sample = new Sample(); $sample->foo(true); } }
どちらも例外クラスを検査するものであるが、エラーメッセージやエラーコードまでは検証できない。
エラーメッセージを検証するには
@expectedExceptionMessage
expectedExceptionMessage()
エラーコードを検証するには
@expectedExceptionCode
expectedExceptionCode()
を使う。
<?php class SampleTest extends TestCase { /** * @expectedException ErrorException * @expectedExceptionMessage 例外のテスト * @expectedExceptionCode Sample::ERROR_CODE_1 */ public function testSample1() { $sample = new Sample(); $sample->foo(true); } public function testSample2() { $this->expectException(ErrorException::class); $this->expectExceptionMessage('例外のテスト'); $this->expectExceptionCode(Sample::ERROR_CODE_1); $sample = new Sample(); $sample->foo(true); } }
trait をテストする
trait をテストしたい。
<?php trait SampleTrait { public function foo () { return 'foo and ' . $this->bar(); } abstract function bar(); }
trait は単独でインスタンス化することができないため、通常のクラスとは異なる手順が必要になる。 テストするには以下の方法がある。
- テストクラスに直接 use する
- 無名クラスを使う
getMockForTrait()
を使う
<?php class SampleTest extends TestCase { // テストクラスに直接 use use SampleTrait; public function testSample1() { $actual = $this->foo(); $this->assertEquals('foo and bar', $actual); } // テストクラスに直接 use する場合、抽象クラスも直接定義しなければならない。 public function bar() { return 'bar'; } // 無名クラスを使う public function testSample2() { $trait = new class{ use SampleTrait; public function bar(){ return 'bar'; } }; $actual = $trait->foo(); $this->assertEquals('foo and bar', $actual); } // getMockForTrait() public function testSample3() { $mockTrait = $this->getMockForTrait(SampleTrait::class); $mockTrait->expects($this->any()) ->method('bar') ->will($this->returnValue('bar')); $actual = $mockTrait->foo(); $this->assertEquals('foo and bar', $actual); } }
テストクラスに直接 use する方法はシンプルではあるが、trait がプロパティや抽象メソッドを定義している場合には余計なメソッドをテストクラスに生やさなければならなくなる。 具象メソッドについても テストクラス のメソッドやプロパティの衝突もあり得るのが怖い。
PHP7 であれば無名クラスが使える。テストクラス に依存しなくてよくなるため、PHP7であればこちらの方がよい。
getMockForTrait()メソッドは引数に渡した trait のモックオブジェクトを返却する。抽象メソッドをモックすることで具象メソッドをテストできるようになる。