Testing an interactive console command in Symfony 2.8
— by Andrea Salini, Web Developer at Caendra Inc.
In this article, we’re going to show how we unit tested an interactive console command in a Symfony 2.8 project.
Disclaimer
If you’re using a newer version of Symfony (3.4 or 4+), it is easier to test this kind of console command thanks to the new CommandTester features. We’ll talk about that at the end of the article.
The issue
The Caendra Dev Team recently faced a problem in which we had to create a console command so that a user could interact with the command itself providing various inputs. We first sought through the Symfony documentation, our Holy Bible, for some indications. Of course, Symfony came to our rescue by giving us the QuestionHelper class. With that class, it was very easy to create an interactive command, but the command had to be carefully tested! Once again we found a section in the Symfony documentation that explained how to test an interactive command, but in this case, we had to add some logic for the test to work. Let’s dig further!
The solution
Following the Symfony documentation, we created a new CommandTester instance with our console command as a parameter, and we tried to get the QuestionHelper.
use Symfony\Component\Console\Tester\CommandTester;class FooCommandTest extends \PHPUnit_Framework_TestCase
{
public function testExecute()
{
// The command to test
$command = new FooCommand(); // Let's create the CommandTested passing the command
$commandTester = new CommandTester($command)
// finally get the QuestionHelper
$helper = $command->getHelper('question');
}
}
But, we’ve got the following error:
Symfony\Component\Console\Exception\LogicException : Cannot retrieve helper "question" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.
Ouch!
Let’s see how we can get around the above error. First of, we had to declare a console application as well as add our console command to the application.
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Console\Application;class FooCommandTest extends \PHPUnit_Framework_TestCase
{
public function testExecute()
{
// create a new Application
$application = new Application();
// add the command to the Application
$application->add(new FooCommand()); $command = $application->find('foo:command:name');
$commandTester = new CommandTester($command)
$helper = $command->getHelper('question');
}
}
Following the documentation, it was easy for us to set up the input stream providing it as a single string separating each input with a new line to simulate the ‘Enter’ keyboard key press. Let’s suppose the console command asks the user for first name, last name, and email.
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Console\Application;class FooCommandTest extends \PHPUnit_Framework_TestCase
{
public function testExecute()
{
$application = new Application();
$application->add(new FooCommand()); $command = $application->find('foo:command:name'); $commandTester = new CommandTester($command)
$helper = $command->getHelper('question'); $input =
'John\n'.
'Doe\n'.
'johndoe@email.com\n'; // we create the input stream and attach it to the helper
$helper->setInputStream($this->getInputStream($input));
$commandTester->execute([]);
} private function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);
fputs($stream, $input);
rewind($stream);
return $stream;
}
}
Ok, but I’m using Symfony 3.4/4..
Don’t worry, as previously mentioned, this solution is suited only if you’re working in a Symfony 2.8 environment. In newer versions of Symfony, like 3.4 or 4+, it’s very easy to test an interactive console command as the CommandTester class provides new features to set the input arguments without the need of the QuestionHelper; so, you also should not need to add the command to a dumb Application object.
This is how we unit tested an interactive Symfony console command in Symfony 2.8. We would be very happy to know if anyone else experienced the same issue and what solution was adopted to solve it. Share your ideas in the comments!