liip / LiipFunctionalTestBundle

Some helper classes for writing functional tests in Symfony
http://liip.ch
MIT License
640 stars 181 forks source link

Getting exit code from runCommand() #329

Closed IwfY closed 6 years ago

IwfY commented 7 years ago

For some command testing I want to evaluate the exit code returned by it. With the current implementation of runCommand() in WebTestCase this is not possible.

So I basically copied the function and used a reference parameter to pass the command's exit code.

/**
 * Builds up the environment to run the given command.
 *
 * alternative to self::runCommand
 *
 * $reuseKernel defaults to true compared to Liip/WebTestCase
 *
 * @param string $name
 * @param array  $params
 * @param bool   $reuseKernel
 * @param int    $runExitCode pointer to be filled with exit code of run command
 *
 * @return string
 */
protected function runCommandWithExitCode($name, array $params = array(), $reuseKernel = true, &$runExitCode)
{
    array_unshift($params, $name);

    if (!$reuseKernel) {
        if (null !== static::$kernel) {
            static::$kernel->shutdown();
        }

        $kernel = static::$kernel = $this->createKernel(array('environment' => $this->environment));
        $kernel->boot();
    } else {
        $kernel = $this->getContainer()->get('kernel');
    }

    $application = new Application($kernel);
    $application->setAutoExit(false);

    // @codeCoverageIgnoreStart
    if ('203' === substr(Kernel::VERSION_ID, 0, 3)) {
        $params = $this->configureVerbosityForSymfony203($params);
    }
    // @codeCoverageIgnoreEnd

    $input = new ArrayInput($params);
    $input->setInteractive(false);

    $fp = fopen('php://temp/maxmemory:' . $this->maxMemory, 'r+');
    $output = new StreamOutput($fp, $this->getVerbosityLevel(), $this->getDecorated());

    $runExitCode = $application->run($input, $output);

    rewind($fp);

    return stream_get_contents($fp);
}

I'd be willing to create a pull request for such functionality if it is wanted in this bundle. I just need some advice how to integrate this feature. Expanding the interface of the current runCommand in the way seen above might not be wanted as it would break backwards compatibility. But having this much duplicate code feels bad too.

Any interest in the feature or preferences how you'd like to have it implemented?

alexislefebvre commented 7 years ago

We may add a variable in order to store the value:

$this->lastExitCode = $application->run($input, $output);

And a method in order to check the exit code:

public function assertSameLastExitCode($expectedExitCode) {
    // the following code has to be rewritten
    PHPUnit::assertSame($expectedExitCode, $this->lastExitCode);
}
Jean85 commented 7 years ago

If we can consider BCs (due to #332), we could do a cleaner solution, changing the return value to an object containing both the output and the exit code as gettable properties.

soullivaneuh commented 7 years ago

changing the return value to an object containing both the output and the exit code as gettable properties.

Also, as the current result is a string, we may implement __toString method to the object returning the same thing with backward compatibility keeping.

fsevestre commented 6 years ago

Using the CommandTester class may solve the issue (see also #219):

This is what I do as a replacement of runCommand for the moment:

protected function executeCommand(Command $command, array $arguments = [], $reuseKernel = false)
{
    if (!$reuseKernel) {
        if (null !== static::$kernel) {
            static::$kernel->shutdown();
        }

        $kernel = static::$kernel = static::createKernel(['environment' => $this->environment]);
        $kernel->boot();
    } else {
        $kernel = $this->getContainer()->get('kernel');
    }

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

    $command = $application->find($command->getName());
    $commandTester = new CommandTester($command);
    $commandTester->execute(
        array_merge(['command' => $command->getName()], $arguments),
        [
            'interative' => false,
            'decorated' => $this->getDecorated(),
            'verbosity' => $this->getVerbosityLevel(),
        ]
    );

    return $commandTester;
}

And to use it:

$commandTester = $this->executeCommand(new MyCommand());

static::assertSame(0, $commandTester->getStatusCode());
static::assertSame($display, $commandTester->getDisplay());

Maybe something like this could be implemented for v2 as replacement of the current implementation of runCommand?

magnetik commented 6 years ago

@alexislefebvre what do you think about flagging it for 2.0 as we have seen that it requires some BC break.

alexislefebvre commented 6 years ago

This has been fixed in #460.