无法测试 Symfony2 控制台命令

发布于 2024-12-28 11:25:21 字数 1669 浏览 0 评论 0原文

作为学习 Symfony2 的一部分,我试图编写一个非常简单的控制台命令,它只运行 phpcs(PHP 代码嗅探器)。

这是扩展 ContainerAwareCommand 的类中的执行函数:

protected function execute(InputInterface $input, OutputInterface $output)
{
    $output->writeln('<info>Generating PHP Code Sniffer report...</info>');
    exec('phpcs ./src > ./app/logs/phpcs.log');

    if ($input->getOption('noprompt') == null) {
        $dialog = $this->getHelperSet()->get('dialog');
        if ($dialog->askConfirmation($output, '<question>Open report in TextMate? (y/n)?</question>', false)) {
            exec('mate ./app/logs/phpcs.log');
        }
    }

    $output->writeln('<info>...done</info>');
}

我能够通过运行来执行控制台命令

app/console mynamespace:ci:phpcs

,并且它运行良好。输出文件按预期生成。

我正在尝试使用以下函数(它是 PHPUnit_Framework_TestCase 的一部分)测试 mynamespace:ci:phpcs 命令:

public function testExecute()
{
    $kernel = new \AppKernel("test", true);
    $kernel->boot();

    $application = new Application($kernel);
    $application->add(new PhpCodeSnifferCommand());

    $command = $application->find('mynamespace:ci:phpcs');
    $commandTester = new CommandTester($command);
    $commandTester->execute(array('command' => $command->getName()));

    // ... Test if output file was created here ... ommitted for brevity ... //
}

但是,当尝试通过单元测试执行它时,它会失败并显示以下输出:

sh: phpcs: command not found

有人知道吗为什么会发生这种情况?

PS:我确实观察到的一件事是,当我注释掉命令中调用“exec”的行时,测试会运行(没有通过,但不会抱怨 phpcs 不存在),所以问题肯定出在执行命令。

PHPUnit 测试是否以其他用户身份运行,而 phpcs 不可用?

As part of learning Symfony2, I'm trying to write a very simple console command that simply runs phpcs (PHP Code Sniffer).

Here's the execute function which is in a class extending ContainerAwareCommand:

protected function execute(InputInterface $input, OutputInterface $output)
{
    $output->writeln('<info>Generating PHP Code Sniffer report...</info>');
    exec('phpcs ./src > ./app/logs/phpcs.log');

    if ($input->getOption('noprompt') == null) {
        $dialog = $this->getHelperSet()->get('dialog');
        if ($dialog->askConfirmation($output, '<question>Open report in TextMate? (y/n)?</question>', false)) {
            exec('mate ./app/logs/phpcs.log');
        }
    }

    $output->writeln('<info>...done</info>');
}

I am able to execute the console command by running

app/console mynamespace:ci:phpcs

and it works perfectly. The output file is generated as expected.

I'm trying to test the mynamespace:ci:phpcs command using the following function (which is part of a PHPUnit_Framework_TestCase):

public function testExecute()
{
    $kernel = new \AppKernel("test", true);
    $kernel->boot();

    $application = new Application($kernel);
    $application->add(new PhpCodeSnifferCommand());

    $command = $application->find('mynamespace:ci:phpcs');
    $commandTester = new CommandTester($command);
    $commandTester->execute(array('command' => $command->getName()));

    // ... Test if output file was created here ... ommitted for brevity ... //
}

However, when trying to execute it via the unit test, it fails with the following output:

sh: phpcs: command not found

Does anyone have an idea why this is happening?

PS: One thing I did observe was, that when I comment out the lines in the command that call 'exec' the test runs through (not passing, but not moaning that phpcs doesn't exist), so the problem is definitely with the exec commands.

Does the PHPUnit tests run as a different user, where phpcs is not available?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

人│生佛魔见 2025-01-04 11:25:21

对于单元测试,您应该考虑模拟对 exec() 的调用。这将加快您的测试速度并避免此类环境问题。对于这种情况,您可以简单地将调用 exec() 的方法添加到您可以模拟测试的类中。

class PhpCodeSnifferCommand extends ...
{
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // ...
        runReport();
        // ...
                viewReport();
        // ...
    }

    protected function runReport() {
        exec('phpcs ./src > ./app/logs/phpcs.log');
    }

    protected function viewReport() {
        exec('mate ./app/logs/phpcs.log');
    }
}

模拟可以更轻松地验证三种可能的路径:

  1. 告诉命令不要提示用户查看报告。
  2. 命令被告知提示;用户说不。
  3. 命令被告知提示;用户说是的。

将每条路径放入其自己的测试中。您可以将通用代码放入测试辅助方法中,以使其更短。

public function testRunsReportWithoutAskingToView()
{
    // ...

    $application = new Application($kernel);
    $phpcs = $this->getMock('PhpCodeSnifferCommand', array('runReport', 'viewReport'));
    $phpcs->expects($this->once())->method('runReport');
    $phpcs->expects($this->never())->method('viewReport');
    $application->add($phpcs);

    // Tell the command not to prompt to view the report ...
}

public function testRunsAndViewsReport()
{
    // ...

    $application = new Application($kernel);
    $phpcs = $this->getMock('PhpCodeSnifferCommand', array('runReport', 'viewReport'));
    $phpcs->expects($this->once())->method('runReport');
    $phpcs->expects($this->once())->method('viewReport');
    $application->add($phpcs);

    // Tell the command to prompt to view and the dialog to hit "Y" for you ...
}

public function testRunsReportButDoesntViewIt()
{
    // ...

    $application = new Application($kernel);
    $phpcs = $this->getMock('PhpCodeSnifferCommand', array('runReport', 'viewReport'));
    $phpcs->expects($this->once())->method('runReport');
    $phpcs->expects($this->never())->method('viewReport');
    $application->add($phpcs);

    // Tell the command to prompt to view and the dialog to hit "N" for you ...
}

For unit tests you should consider mocking the calls to exec(). This will speed up your tests and avoid environmental issues such as this. For this case you can simply add methods that call exec() to your class that you can mock for the tests.

class PhpCodeSnifferCommand extends ...
{
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // ...
        runReport();
        // ...
                viewReport();
        // ...
    }

    protected function runReport() {
        exec('phpcs ./src > ./app/logs/phpcs.log');
    }

    protected function viewReport() {
        exec('mate ./app/logs/phpcs.log');
    }
}

Mocking makes it easier to validate the three possible paths:

  1. The command is told not to prompt the user to view the report.
  2. The command is told to prompt; the user says no.
  3. The command is told to prompt; the user says yes.

Place each path in its own test. You could put the common code into a test helper method to make this much shorter.

public function testRunsReportWithoutAskingToView()
{
    // ...

    $application = new Application($kernel);
    $phpcs = $this->getMock('PhpCodeSnifferCommand', array('runReport', 'viewReport'));
    $phpcs->expects($this->once())->method('runReport');
    $phpcs->expects($this->never())->method('viewReport');
    $application->add($phpcs);

    // Tell the command not to prompt to view the report ...
}

public function testRunsAndViewsReport()
{
    // ...

    $application = new Application($kernel);
    $phpcs = $this->getMock('PhpCodeSnifferCommand', array('runReport', 'viewReport'));
    $phpcs->expects($this->once())->method('runReport');
    $phpcs->expects($this->once())->method('viewReport');
    $application->add($phpcs);

    // Tell the command to prompt to view and the dialog to hit "Y" for you ...
}

public function testRunsReportButDoesntViewIt()
{
    // ...

    $application = new Application($kernel);
    $phpcs = $this->getMock('PhpCodeSnifferCommand', array('runReport', 'viewReport'));
    $phpcs->expects($this->once())->method('runReport');
    $phpcs->expects($this->never())->method('viewReport');
    $application->add($phpcs);

    // Tell the command to prompt to view and the dialog to hit "N" for you ...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文