Skip to content
This repository

Adding description of how to test a command which expects user input #2398

Merged
merged 3 commits into from about 1 year ago

5 participants

Sebastian Marcin Chwedziak Ryan Weaver Wouter J Christophe Coevoet
Sebastian

This PR adds a description on testing a command which uses the DialogHelper and expects some user input.

Q A
Doc fix? no
New docs? yes
Applies to 2.0+
Fixed tickets #2351

One thing I'm not sure about is if I should add a link to the section about command testing. If so, where should I add it?

components/console/helpers/dialoghelper.rst
((7 lines not shown))
  101 +
  102 +Testing a command which expects input
  103 +-------------------------------------
  104 +
  105 +If you want to write a unit test for a command which expects some kind of input
  106 +from the command line, you need to overwrite the HelperSet used by the command::
  107 +
  108 + use Symfony\Component\Console\Helper\DialogHelper;
  109 + use Symfony\Component\Console\Helper\HelperSet;
  110 +
  111 + // ...
  112 +
  113 + public function testExecute()
  114 + {
  115 +
  116 + // ..
1
Wouter J Collaborator
WouterJ added a note

please use 3 dots

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
components/console/helpers/dialoghelper.rst
((6 lines not shown))
  100 +be able to proceed if her input is valid.
  101 +
  102 +Testing a command which expects input
  103 +-------------------------------------
  104 +
  105 +If you want to write a unit test for a command which expects some kind of input
  106 +from the command line, you need to overwrite the HelperSet used by the command::
  107 +
  108 + use Symfony\Component\Console\Helper\DialogHelper;
  109 + use Symfony\Component\Console\Helper\HelperSet;
  110 +
  111 + // ...
  112 +
  113 + public function testExecute()
  114 + {
  115 +
1
Wouter J Collaborator
WouterJ added a note

unneeded empty line

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Wouter J WouterJ commented on the diff
components/console/helpers/dialoghelper.rst
((5 lines not shown))
  100 +be able to proceed if her input is valid.
  101 +
  102 +Testing a command which expects input
  103 +-------------------------------------
  104 +
  105 +If you want to write a unit test for a command which expects some kind of input
  106 +from the command line, you need to overwrite the HelperSet used by the command::
  107 +
  108 + use Symfony\Component\Console\Helper\DialogHelper;
  109 + use Symfony\Component\Console\Helper\HelperSet;
  110 +
  111 + // ...
  112 +
1
Wouter J Collaborator
WouterJ added a note

unneeded empty line here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Wouter J WouterJ commented on the diff
components/console/helpers/dialoghelper.rst
((20 lines not shown))
  114 + // ...
  115 + $commandTester = new CommandTester($command);
  116 +
  117 + $dialog = new DialogHelper();
  118 + $dialog->setInputStream($this->getInputStream('Test\n'));
  119 + // Equals to a user inputing "Test" and hitting ENTER
  120 + // If you need to enter a confirmation, "yes\n" will work
  121 +
  122 + $command->setHelperSet(new HelperSet(array($dialog)));
  123 +
  124 + $commandTester->execute(array('command' => $command->getName()));
  125 +
  126 + // $this->assertRegExp('/.../', $commandTester->getDisplay());
  127 + }
  128 +
  129 + protected function getInputStream($input)
2
Wouter J Collaborator
WouterJ added a note

Asserting is not something that's usually done in a Command test. Mock objects are a better practise

I copied this from the description on how to test commands. I think as a comment it is ok because it ilustrates that the command is now being executed and one can either test the outcome or end the test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Wouter J WouterJ commented on the diff
components/console/helpers/dialoghelper.rst
((21 lines not shown))
  115 + $commandTester = new CommandTester($command);
  116 +
  117 + $dialog = new DialogHelper();
  118 + $dialog->setInputStream($this->getInputStream('Test\n'));
  119 + // Equals to a user inputing "Test" and hitting ENTER
  120 + // If you need to enter a confirmation, "yes\n" will work
  121 +
  122 + $command->setHelperSet(new HelperSet(array($dialog)));
  123 +
  124 + $commandTester->execute(array('command' => $command->getName()));
  125 +
  126 + // $this->assertRegExp('/.../', $commandTester->getDisplay());
  127 + }
  128 +
  129 + protected function getInputStream($input)
  130 + {
3
Wouter J Collaborator
WouterJ added a note

unneeded empty line

I'm not sure which line you refer to! I guess between methods there should be an empty line, bracets should also be on their own line. I don't see any other empty lines?

Wouter J Collaborator
WouterJ added a note

:) there was an empty line somewhere here, but I commented on the oldest commit, but it is showing the comments on the new commit. Just a github bug..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
components/console/helpers/dialoghelper.rst
((9 lines not shown))
  103 +-------------------------------------
  104 +
  105 +If you want to write a unit test for a command which expects some kind of input
  106 +from the command line, you need to overwrite the HelperSet used by the command::
  107 +
  108 + use Symfony\Component\Console\Helper\DialogHelper;
  109 + use Symfony\Component\Console\Helper\HelperSet;
  110 +
  111 + // ...
  112 + public function testExecute()
  113 + {
  114 + // ...
  115 + $commandTester = new CommandTester($command);
  116 +
  117 + $dialog = new DialogHelper();
  118 + $dialog->setInputStream($this->getInputStream('Test\n'));
1
Christophe Coevoet
stof added a note

you don't need to overwrite the whole helperSet as all you need is calling a setter on the dialog helper:

$dialog = $command->getHelper('dialog');
$dialog->setInputStream(...)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Marcin Chwedziak

Impressive. I tried mocking DialogHelper using Mockery, with no luck, but this seems to be cleaner and easier. Unfortunately, this won't work if the order of questions for which we want user to provide answers to is non-deterministic. Is there any chance of getting a description about how to use Mockery in order to create more sophisticated tests for our commands?

Sebastian

I see your problem. Actually, testing the command itself with Mockery doesn't make much sense, much in the same way testing a controller in a unit test doesn't make much sense. Both are more or less boilerplate code, connecting your services to some front end (http or cli).

You could see if you can read the output stream and parse it, so you know which question is asked. Then you could provide some sort of mapping (if question x, answer with y). This of course means that rewriting one of your questions would break tests, but I guess it should be possible.

Anyway, I think this should be either discussed on the mailing list or postet to SO as to not pollute the PR.

Ryan Weaver weaverryan merged commit 61b0787 into from
Ryan Weaver
Collaborator

Thanks Sebastian - awesome work!

Sebastian Sgoettschkes deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 40 additions and 1 deletion. Show diff stats Hide diff stats

  1. +40 1 components/console/helpers/dialoghelper.rst
41 components/console/helpers/dialoghelper.rst
Source Rendered
@@ -97,4 +97,43 @@ You can set the max number of times to ask in the ``$attempts`` argument.
97 97 If you reach this max number it will use the default value, which is given
98 98 in the last argument. Using ``false`` means the amount of attempts is infinite.
99 99 The user will be asked as long as he provides an invalid answer and will only
100   -be able to proceed if her input is valid.
  100 +be able to proceed if her input is valid.
  101 +
  102 +Testing a command which expects input
  103 +-------------------------------------
  104 +
  105 +If you want to write a unit test for a command which expects some kind of input
  106 +from the command line, you need to overwrite the HelperSet used by the command::
  107 +
  108 + use Symfony\Component\Console\Helper\DialogHelper;
  109 + use Symfony\Component\Console\Helper\HelperSet;
  110 +
  111 + // ...
  112 + public function testExecute()
  113 + {
  114 + // ...
  115 + $commandTester = new CommandTester($command);
  116 +
  117 + $dialog = $command->getHelper('dialog');
  118 + $dialog->setInputStream($this->getInputStream('Test\n'));
  119 + // Equals to a user inputing "Test" and hitting ENTER
  120 + // If you need to enter a confirmation, "yes\n" will work
  121 +
  122 + $commandTester->execute(array('command' => $command->getName()));
  123 +
  124 + // $this->assertRegExp('/.../', $commandTester->getDisplay());
  125 + }
  126 +
  127 + protected function getInputStream($input)
  128 + {
  129 + $stream = fopen('php://memory', 'r+', false);
  130 + fputs($stream, $input);
  131 + rewind($stream);
  132 +
  133 + return $stream;
  134 + }
  135 +
  136 +By setting the inputStream of the `DialogHelper`, you do the same the
  137 +console would do internally with all user input through the cli. This way
  138 +you can test any user interaction (even complex ones) by passing an appropriate
  139 +input stream.

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.