4

Symfony2の学習の一環として、phpcs(PHPコードスニファー)を実行する非常に単純なコンソールコマンドを作成しようとしています。

ContainerAwareCommandを拡張するクラスにあるexecute関数は次のとおりです。

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:私が観察したことの1つは、「exec」を呼び出すコマンドの行をコメントアウトすると、テストが実行されることです(合格しませんが、phpcsが存在しないことを嘆くことはありません)。したがって、問題は間違いなくexecコマンド。

PHPUnitテストは、phpcsが利用できない別のユーザーとして実行されますか?

4

1 に答える 1

5

単体テストの場合は、への呼び出しをモックすることを検討する必要があります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');
    }
}

モッキングにより、次の3つの可能なパスの検証が容易になります。

  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 ...
}
于 2012-01-22T20:34:47.380 に答える