给定以下实现:
class Foo {
public function helper() {
// Does something with external side effects, like updating
// a database or altering the file system.
}
public function bar() {
if ($this->helper() === FALSE) {
throw new Exception(/* ... */);
}
}
}
我将如何进行单元测试Foo::bar()
而不产生测试期间的副作用Foo::helper()
?
我知道我可以模拟Foo
和存根Foo::helper()
:
public function testGoodBar() {
$mock = $this->getMock('Foo', array('helper'));
$this->expects($this->once())
->method('helper')
->will($this->returnValue(TRUE));
$this->assertTrue($mock->bar());
}
...但这使得测试对引入其他可能有副作用的方法的代码更改开放。然后,如果在没有更新的情况下再次运行测试,测试本身就会产生永久性的副作用。
我也可以 mock Foo
,这样它的所有方法都会被嘲笑并且不会产生副作用:
public function testGoodBar() {
$mock = $this->getMock('Foo');
$this->expects($this->once())
->method('helper')
->will($this->returnValue(TRUE));
$this->assertTrue($mock->bar());
}
...但随后甚至Foo::bar()
被嘲笑,这很糟糕,因为这是我们想要测试的方法。
我能想出的唯一解决方案是显式地模拟除被测方法之外的所有方法:
public function testGoodBar() {
$mock = $this->getMock('Foo', array_diff(
get_class_methods('Foo'),
'bar'
));
$this->expects($this->once())
->method('helper')
->will($this->returnValue(TRUE));
$this->assertTrue($mock->bar());
}
...但这看起来很笨拙,我觉得我错过了一些明显的东西。