我使用zend 2.2和zend / console来创建cli脚本。该脚本提示用户输入名称或电子邮件地址等信息,并使用以下代码
$name = Line::prompt('Enter Name: ', false, 100);
当用户使用脚本时,这可以正常工作。
我的问题是尝试对此进行单元测试。此时,当我在单元测试中调用调度时,它只是挂起并等待用户输入响应。显然这是一个问题。
我的代码是:
public function testCliScript()
{
$consoleMock = $this->getConsoleMock();
$consoleMock->expects($this->any())->method('writeLine');
$this->dispatch('run cli--name=test --email=test@example.com');
}
最终调用
protected function getName()
{
$name = $this->getRequest()->getParam('name');
$nameOk = !empty($name);
while (!$nameOk) {
$nameOk = false;
$name = Line::prompt('Enter Name: ', false, 100);
// tie into filter
if (strlen($name) < 1) {
$this->getConsole()->writeLine('Name is Too Short');
continue;
}
$nameOk = true;
};
return $name;
}
如果有人对如何测试这一点有任何建议,我们将不胜感激。
谢谢
答案 0 :(得分:1)
这是Dipendency Inversion Principle:testability的主要论据之一。
你的提示对象是你的SUT的硬依赖,因此,要正确测试SUT,你需要将两者分离。
通过构造函数使用Dependency Injection的示例:
// uses omitted for brevity
class SomeController extends AbstractConsoleController
{
protected $namePrompt;
public function __construct(PromptInterface $namePrompt)
{
$this->namePrompt = $namePrompt;
}
protected function getName()
{
// etc etc
$name = $this->namePrompt->show();
}
}
所以你可以在测试中注入一个提示模拟:
// uses omitted for brevity
class SomeControllerTest extends TestCase
{
protected $controller;
protected $namePromptMock;
public function setUp()
{
// etc etc
$this->namePromptMock = $this->getMock(PromptInterface::class);
$this->controller = new SomeController($this->namePromptMock);
// etc etc
}
public function testSomeAction()
{
$this->namePromptMock->expects($this->atLeastOnce())
->method('show')
->willReturn('a fake name');
// dispatch, assertions on the response, etc etc
}
}
还有其他方法可以实现依赖倒置,即在这种情况下控制器可能需要一大堆提示,因此不是注入每个提示实例(这是不方便和丑陋),你可以使用一个小插件管理器作为一个服务定位器,或使用某种工厂,你可以轻松地模拟这样的抽象,以便它在被问到时只返回提示模拟。