我的问题是关于单元测试。假设我们有以下课程;
class X
{
public function p1(){
//logic
$a = $this->p2();
//more logic
}
public function p2(){
//even more logic
}
}
在为p1方法编写单元测试时,我应该模拟p2方法吗?
我在想的是,为p1方法编写的测试应该只执行并测试p1方法而不是p2。但是为了意识到我应该得到一个类X的模拟并在下面的模拟实例上调用p1方法。
$xMock = $this->getMockBuilder('\X')
->setMethods(array('p2'))
->getMock();
$xMock->expects($this->any())
->method('p2')
->will($this->returnValue($value));
$resultTobeAsserted = $xMock->p1();
不幸的是,这样做对我来说有点不对劲。我与同事讨论了这个话题,归结为你如何定义你的SUT(被测系统)。 如果测试人员将正在测试的特定方法视为SUT,那么从SUT调用的其他方法似乎是依赖关系,并且测试人员自然会想要模拟它们。另一方面,如果测试者将整个类视为SUT,那么这些方法调用将成为测试的一部分,因此没有任何理由来模拟它们。
这个结论是否正确?哪种思维会产生更强大的单元测试?
答案 0 :(得分:3)
在为p1方法编写单元测试时,我应该模拟p2方法吗?
否强>
您正在调用类上的方法,并且您希望事情发生。
通过模拟p2,您可以对类的实现细节进行例外处理。
不幸的是,这样做对我来说有点不对。
我说它落在了它上面。
哪种思维会产生更强大的单元测试?
如果测试类的可观察行为,则确保在更改类的实现时类仍然执行它应该执行的操作。多数人坚强。
如果您测试一个方法并模拟该方法的部分实现(内部方法调用),那么您测试一个特定的实现,如果测试失败,您不知道外部行为是否发生了变化。
我在以下文章中详细介绍了这一点:
The UNIT in unit testing.
详细说明了为什么我认为测试行为而不是方法的重要性。
简而言之
PHP中的单元测试是关于测试类的可观察行为!
<强>行为:强>
测试那些东西。无视实施细节。
答案 1 :(得分:2)
如果测试人员将正在测试的特定方法视为SUT,那么从SUT调用的其他方法似乎是依赖关系,并且测试人员自然会想要模拟它们。
如果有支持这种分离的参数,可以使用相同的参数将类重构为两个类,这正是你应该做的。
答案 2 :(得分:1)
你的想法是正确的。如果你想要测试方法p1
,你不关心p2
因为你认为它已经在另一个测试中进行了测试。所以,你可以模拟/存根p2
。但你必须记住,你应该只模拟/存根p2
。所以你的例子看起来应该更像:
$xMock = $this->getMockBuilder('\X')
->setMethods(array('p2'))
->getMock();
$xMock->expects($this->any())
->method('p2')
->will($this->returnValue($value));
$resultTobeAsserted = $xMock->p1();