我正在尝试将一堆测试从 SimpleTest 迁移到 PHPUnit,我想知道 SimpleTest 的 部分模拟是否有等效项。
我似乎在文档中找不到任何表明此功能可用的内容,但我突然想到我可以只使用一个子类。这是好主意还是坏主意?
class StuffDoer {
protected function doesLongRunningThing() {
sleep(10);
return "stuff";
}
public function doStuff() {
return $this->doesLongRunningThing();
}
}
class StuffDoerTest {
protected function doesLongRunningThing() {
return "test stuff";
}
}
class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
public function testStuffDoer() {
$sd = new StuffDoerTest();
$result = $sd->doStuff();
$this->assertEquals($result, "test stuff");
}
}
从阅读链接页面来看,SimpleTest 部分模拟似乎是仅覆盖部分方法的模拟。如果这是正确的,则该功能由普通的 PHPUnit 模拟处理。
在
PHPUnit_Framework_TestCase
中,您可以使用 创建一个模拟
$mock = $this->getMock('Class_To_Mock');
这会创建一个模拟实例,其中所有方法都不执行任何操作并返回 null。如果您只想重写某些方法,则
getMock
的第二个参数是要重写的方法数组。
$mock = $this->getMock('Class_To_Mock', array('insert', 'update'));
将创建
Class_To_Mock
的模拟实例,并删除 insert
和 update
函数,准备好指定它们的返回值。
此信息位于 phpunit 文档。
注意,这个答案显示了更多最新的代码示例,适用于从 5.4 开始的 PHPUnit 版本
PHPUnit_Framework_TestCase::getMock
自 phpunit 5.4 起已弃用。我们可以使用 setMethods
来代替。
setMethods(array $methods) 可以在 Mock Builder 对象上调用,以指定要替换为可配置测试替身的方法。其他方法的行为不会改变。如果调用 setMethods(null),则不会替换任何方法。
https://phpunit.de/manual/current/en/test-doubles.html
$observer = $this->getMockBuilder(Observer::class)
->setMethods(['update'])
->getMock();
注意上面的
getMock
是PHPUnit_Framework_MockObject_MockBuilder::getMock
。 (phpunit5.6)
方法
setMethods
已被弃用。现在它可以工作了:
$mock = $this->getMockBuilder(ClassToMock::class)
->onlyMethods(['insert', 'update'])
->getMock();
我认为 PHPUnit 不支持对被测系统的部分模拟。如果您尝试隔离方法,那么我确信您的实现是有效的 - 我也这样做了。
但是,出于几个原因,我尽量避免这样做。
首先,它将您的测试与类的内部实现紧密结合。你真的关心是否调用了名为
doesLongRunningThing
的方法,还是“LongRunningThing”完成更重要?
其次,当我遇到这个问题时,我总是想知道是否有一个班级在做两个班级的工作。 提取类重构可能是有序的。如果
doesLongRunningThing()
成为自己的类,即使使用单一方法,测试也会变得更加容易。
我相信解决方案是注入您的 SUT 所依赖的服务 (http://en.wikipedia.org/wiki/Dependency_injection)。这也使得
DoesLongRunningThing
实现更易于测试。
在不跳入界面的情况下,我会这样做:
class DoesLongRunningThing {
public function execute() {
sleep(10);
return "stuff";
}
}
class StuffDoer {
protected $doesLongRunningThing;
public function setLongRunningThinger(DoesLongRunningThing $obj) {
$this->doesLongRunningThing = $obj;
}
public function doStuff() {
return $this->doesLongRunningThing->execute();
}
}
现在很容易嘲笑了:
class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
public function testStuffDoer() {
$dlrtMock = $this->getMock('DoesLongRunningThing');
$dlrtMock->expects($this->any())->will($this->returnValue("test stuff"));
$sd = new StuffDoer();
$sd->setLongRunningThinger($dlrtMock);
$result = $sd->doStuff();
$this->assertEquals($result, "test stuff");
}
}
您正在嘲笑的类可能不在范围内(我遇到了这个问题)。解决后,我能够模拟一个函数并测试另一个函数的实际逻辑。
$mockController = $this->getMockBuilder('ControllerClassName')
->setMethods(['functionToMock'])
->getMock();
$result = $mockController->anotherFunction();
$this->assertEquals(true, $result);