ユニットテストを行うときは、実装の詳細をテストしないようにすることが非常に重要です。代わりに、コードのパブリックインターフェイスのみをテストするように制限する必要があります。なんで?実装の詳細は頻繁に変更されますが、APIはめったに変更されないためです。実装の詳細をテストするということは、それらの実装が変更されたときに常にテストを書き直す必要があり、これをやり続ける必要がないことを意味します。
では、これはOPのコードにとって何を意味するのでしょうか。Pager::next
publicメソッドを見てみましょう。Pager
クラスAPIを使用するコードは、例外をスローする必要があるかどうかをどのように決定するかを気にしません。何かが間違っている場合に実際に例外をスローするPager::next
ことだけを気にします。Pager::next
メソッドがスローする決定にどのように到達するかをテストしたくありませんOutOfBoundsException
-これは実装の詳細です。適切な場合にのみ、テストする必要があります。
したがって、このシナリオをテストするために、Pager::next
がスローされる状況をシミュレートします。これを実現するには、「テストシーム」と呼ばれるものを実装するだけです。..。
<?php
class Pager
{
protected $i;
public function next()
{
if ($this->isValid()) {
$this->i++;
} else {
throw new OutOfBoundsException();
}
}
protected function isValid() {
return $this->i < 3;
}
}
上記のコードでは、保護されたPager::isValid
メソッドがテストシームです。これにより、テスト目的でラッチできるコード(名前の由来)の継ぎ目が公開されます。新しいテストシームとPHPUnitのモックAPIを使用すると、Pager::next
の無効な値に対して例外をスローするテスト$i
は簡単です。
class PagerTest extends PHPUnit_Framework_TestCase
{
/**
* @covers Pager::next
* @expectedException OutOfBoundsException
*/
public function testNextThrowsExceptionOnInvalidIncrementValue() {
$pagerMock = $this->getMock('Pager', array('isValid'));
$pagerMock->expects($this->once())
->method('isValid')
->will($this->returnValue(false));
$pagerMock->next();
}
}
このテストでは、実装メソッドが現在の増分が無効であると判断する方法を特に気にしないことに注意してください。このテストは、呼び出されたときPager::isValid
に返されるメソッドをモックするだけなので、パブリックメソッドが例外をスローするはずのときに例外をスローすることをテストできます。false
Pager::next
PHPUnitモックAPIについては、PHPUnitマニュアルのTestDoublesセクションで詳しく説明されています。APIは、世界の歴史の中で最も直感的なものではありませんが、繰り返し使用することで、一般的に理にかなっています。