Question

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?

Was it helpful?

Solution

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 ...
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top