在进行单元测试时,避免测试实现细节非常重要。相反,您希望将自己限制为仅测试代码的公共接口。为什么?因为实现细节经常改变,但你的 API 应该很少改变。测试实现细节意味着随着这些实现的变化,您将不得不不断地重写您的测试,并且您不希望被困在这样做。
那么这对 OP 的代码意味着什么?让我们看一下公共Pager::next
方法。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 手册的 Test Doubles 部分中全面介绍。API 并不是世界历史上最直观的东西,但经过多次重复使用,它通常是有意义的。