假设我有这门课程:
class SomeClass
{
// Top level function
public function execute($command)
{
// Get output from system tool
$output = $this->runTool($command);
// Check output for errors
if ($this->hasError($output))
return false;
// And parse success response from tool
return $this->parseOutput($output);
}
// There we're make a call to system
private function runTool($command)
{
return `/some/system/tool $command`;
}
[...]
}
我不想在我的测试中运行系统工具,我想用预定义的输出替换系统调用。
所以,问题是 - 我应该创建另一个类,在其中移动系统调用并在测试中模拟该类,或者我只能模拟我将要测试的类的函数?
当然,这两种方法都可行,但是哪种方法可以更好地为测试目的服务呢?
答案 0 :(得分:2)
如果您遵循单一责任原则,您就不会遇到此问题。您的课程不需要知道如何进行系统调用,因此您必须使用另一个类。你嘲笑那个。
IMO,在大多数情况下,当你需要模拟受保护的或私有的方法时,他们会做一些应该进入另一个类并被嘲笑的东西。
答案 1 :(得分:0)
我会说这实际上取决于您的基础设施。有时最好使用Mock,有时候使用Stub。
如果是这样的话,你要测试的类包含这个不需要的方法 - 使用Mock并仅模拟这一个函数。这将使您确信,对该类所做的任何更改都将由测试处理。
如果不需要的函数是注入服务或另一个类的一部分,而不是此特定测试的域,则可以创建存根。
答案 2 :(得分:0)
您无法测试私有方法,您可以使用变通方法并按照此article中所述通过反射调用它,并在此SO QUESTION中讨论
但我建议您将方法可见性更改为protected并仅模拟runTool方法的行为。
例如,假设您的类的以下修改版本(我不知道其他方法如何工作,所以我想您要测试他们的行为并以此实现为例):
<?php
namespace Acme\DemoBundle\Service;
class SomeClass
{
// Top level function
public function execute($command)
{
// Get output from system tool
$output = $this->runTool($command);
// Check output for errors
if ($this->hasError($output))
return false;
// And parse success response from tool
return $this->parseOutput($output);
}
// There we're make a call to system
protected function runTool($command)
{
return `/some/system/tool $command`;
}
private function hasError($output)
{
return $output == "error";
}
private function parseOutput($output)
{
return json_decode($output);
}
}
假设以下测试用例:
<?php
namespace Acme\DemoBundle\Tests;
class SomeClassTest extends \PHPUnit_Framework_TestCase {
public function testCommandReturnError()
{
$mock = $this->getMockBuilder('Acme\DemoBundle\Service\SomeClass')
->setMethods(array('runTool'))
->disableOriginalConstructor()
->getMock()
;
$mock
->expects($this->exactly(1))
->method('runTool')
->with("commandName")
->will($this->returnValue("error"));
$this->assertFalse($mock->execute("commandName"));
}
public function testCommandReturnCorrectValue()
{
$mock = $this->getMockBuilder('Acme\DemoBundle\Service\SomeClass')
->setMethods(array('runTool'))
->disableOriginalConstructor()
->getMock()
;
$mock
->expects($this->exactly(1))
->method('runTool')
->with("commandName")
->will($this->returnValue('{"title":"myTitle"}'));
$returnValue = $mock->execute("commandName");
$this->assertEquals("myTitle", $returnValue->title);
}
}
希望这个帮助