Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SymfonyStyle] Unable to test a command in phpUnit #26885

Closed
FroggDev opened this issue Apr 11, 2018 · 9 comments

Comments

Projects
None yet
5 participants
@FroggDev
Copy link

commented Apr 11, 2018

Q A
Bug report? no
Feature request? yes
BC Break report? no
RFC? no
Symfony version 4.1

Hi, iam trying to test a Command using symfony style
https://github.com/FroggDev/Symfony_StockManager/blob/master/src/Command/UserManager.php

I tried a lot of things without success in phpUnit with KernelTestCase.
This is the version of the code i am using to test the command:

       // Get the command tester
        $commandTester = new CommandTester(self::$command);

        // Set input scenario
        $commandTester->setInputs([0]); // display user list
        $commandTester->setInputs([5]); // exit

        echo "BEFORE";

        // Execute the command
        $commandTester->execute(['command' => self::$command->getName()]);

        echo "AFTER";

Based on https://symfony.com/doc/current/components/console/helpers/questionhelper.html

The string "Before" is displayed but after the excecute no more output are displayed.
Even any phpUnit output.

Is there anyway to solve this trouble ?

Thanks

@chalasr

This comment has been minimized.

Copy link
Member

commented Apr 11, 2018

You are using exit() at several places in your command, that exits the current process (being phpunit here). A command should not exit, it should return proper status codes or throw exceptions. Application is responsible for exiting (explicitly disabled when using the CommandTester).

@FroggDev

This comment has been minimized.

Copy link
Author

commented Apr 11, 2018

I will try this today, and will tell you the result, thanks.

@FroggDev

This comment has been minimized.

Copy link
Author

commented Apr 11, 2018

I have more news:

i updated the Command to send an exit code instead of use the exit() function from php
https://github.com/FroggDev/Symfony_StockManager/blob/master/src/Command/UserManager.php

and i updated the phpUnit test for it
https://github.com/FroggDev/Symfony_StockManager/blob/master/tests/Command/UserManagerTest.php

Now i can see correctly phpUnit information, but in my option there is a trouble :

Only 1 setInputs is triggered each execute and it stop the process without executing the input.

This is the test i have done to try to find where the bug is:

  • Context
    When I start the command i display a main menu which wait for an user input.
    0 display a user list then back to the main menu which wait for an user input.
    2 enable user then back to the main menu which wait for an user input.
    5 exit the command

  • Test
    If i dont set input phpunit script stay blocked (which seems normal as the script wait for an input)

  • Test Exit Command
    If i set one setInputs([5]) the script will execute and exit the command (seems ok, but in fact it is not i will explain after)

  • Test Display User List
    If i set one setInputs([0]) the script will execute and exit the command (should not as it return on the step which wait for user input)

  • Test Enable user
    If i set one setInputs([2]) the script will execute and exit the command (should not as it return on the step which wait for user input & the setEnable user function hasn't been called)

  • More
    What ever i do the output is always the same:

Welcome to User Role Manager\r\n
============================\r\n
\r\n
 Select an action [Display user list]:\r\n
  [0] Display user list\r\n
  [1] Display user list (Version 4.1)\r\n
  [2] Enable user account (require user id)\r\n
  [3] Add user role (require user id)\r\n
  [4] Remove user role (require user id)\r\n
  [5] Exit\r\n
 > 5\r\n

This is the main menu output which ask for user input. After nothing is outputed

  • Even More

i tried something simplier to identify where something wrong happend:

 Select an action [Display user list]:\r\n
  [0] Echo a basic message\r\n

when i do setInputs([0]), and then try to display output, i have the menu but not the result of the function "echo a basic message".

I hope there is enought information to understand what s could be wrong.
Thanks

@FroggDev

This comment has been minimized.

Copy link
Author

commented Apr 12, 2018

Even without Symfony style i get a strange behavior:

  • Command file :
    protected function execute(InputInterface $input, OutputInterface $output): void
    {
        $helper = $this->getHelper('question');
        $question = new ConfirmationQuestion('Continue with this action?', false);
        if (!$helper->ask($input, $output, $question)) {return;}

        $helper = $this->getHelper('question');
        $question = new ConfirmationQuestion('Continue with this action?', false);
        if (!$helper->ask($input, $output, $question)) {return; }
   }
  • phpunit test file
    public function testFake()
    {
        // Init
        $application = new Application(self::$kernel);
        $application->setAutoExit(true);
        $command = $application->find('app:userManager');
        $commandTester = new CommandTester($command);

        // Equals to a user inputting "y" and hitting ENTER
        $commandTester->setInputs(array('y'));
        // Equals to a user inputting "y" and hitting ENTER
        $commandTester->setInputs(array('y'));

        $commandTester->execute(array('command' => self::$command->getName()));

        var_dump( $commandTester->getDisplay());
    }

Result:

There was 1 error:

1) App\Tests\Command\UserManagerTest::testFake
Symfony\Component\Console\Exception\RuntimeException: Aborted

C:\symfony\Symfony_StockManager\vendor\symfony\console\Helper\QuestionHelper.php:118
C:\symfony\Symfony_StockManager\vendor\symfony\console\Helper\QuestionHelper.php:63
C:\symfony\Symfony_StockManager\src\Command\UserManager.php:192
C:\symfony\Symfony_StockManager\vendor\symfony\console\Command\Command.php:252
C:\symfony\Symfony_StockManager\vendor\symfony\console\Tester\CommandTester.php:77
C:\symfony\Symfony_StockManager\tests\Command\UserManagerTest.php:81
@chalasr

This comment has been minimized.

Copy link
Member

commented Apr 12, 2018

For your last example, your command is asking two questions but only one is answered by your test. Calling setInputs() twice for answering two questions is not good as te second call erases the first one, it needs to be setInputs(['y', 'y']);.

I'll give a try to your previous examples today.

@FroggDev

This comment has been minimized.

Copy link
Author

commented Apr 12, 2018

        // Init
        $application = new Application(self::$kernel);
        $application->setAutoExit(true);
        $command = $application->find('app:userManager');
        $commandTester = new CommandTester($command);

        echo "BEFORE ?";

        // Equals to a user inputting "y" and hitting ENTER
        $commandTester->setInputs(array('y','y'));
        $commandTester->execute(array('command' => self::$command->getName()));

        echo "HERE ??";

I tryed like this and the phpunit test exited without any information, only the "BEFORE ?" is outputed in phpunit

By the way the documentation say that each question should have a setInputs:

from https://symfony.com/doc/current/components/console/helpers/questionhelper.html

    // ...
    $commandTester = new CommandTester($command);

    // Equals to a user inputting "Test" and hitting ENTER
    $commandTester->setInputs(array('Test'));

    // Equals to a user inputting "This", "That" and hitting ENTER
    // This can be used for answering two separated questions for instance
    $commandTester->setInputs(array('This', 'That'));

    // For simulating a positive answer to a confirmation question, adding an
    // additional input saying "yes" will work
    $commandTester->setInputs(array('yes'));

    $commandTester->execute(array('command' => $command->getName()));

    // $this->assertRegExp('/.../', $commandTester->getDisplay());
@Gemorroj

This comment has been minimized.

@chalasr

This comment has been minimized.

Copy link
Member

commented Sep 8, 2018

@Gemorroj that's a bug, using SymfonyStyle::choice() instead of askQuestion() would work as expected. I'm looking at it.

@Gemorroj

This comment has been minimized.

Copy link

commented Sep 8, 2018

@chalasr Yes, I did not have time to fully understand, but it seems there https://github.com/symfony/console/blob/4.1/Helper/SymfonyQuestionHelper.php#L59-L61 should be something like this:

$choices = $question->getChoices();
$defaultValue = \array_search($default, $choices, true);
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($choices[$defaultValue]));

chalasr added a commit that referenced this issue Sep 8, 2018

bug #28401 [Console] Fix SymfonyQuestionHelper::askQuestion() with ch…
…oice value as default (chalasr)

This PR was merged into the 2.8 branch.

Discussion
----------

[Console] Fix SymfonyQuestionHelper::askQuestion() with choice value as default

| Q             | A
| ------------- | ---
| Branch?       | 2.8
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #26885
| License       | MIT
| Doc PR        | n/a

There is an inconsistency between `SymfonyStyle::askQuestion(new ChoiceQuestion(...))` and `SymfonyStyle::choice(...)`, the former does not support to have a choice value as default instead of a choice key while the latter handles both.
This is causing an `undefined index` notice breaking interactive command testing, fixed here.

Commits
-------

c51dda0 [Console] Fix SymfonyQuestionHelper::askQuestion() with choice value as default

symfony-splitter pushed a commit to symfony/console that referenced this issue Sep 8, 2018

bug #28401 [Console] Fix SymfonyQuestionHelper::askQuestion() with ch…
…oice value as default (chalasr)

This PR was merged into the 2.8 branch.

Discussion
----------

[Console] Fix SymfonyQuestionHelper::askQuestion() with choice value as default

| Q             | A
| ------------- | ---
| Branch?       | 2.8
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | symfony/symfony#26885
| License       | MIT
| Doc PR        | n/a

There is an inconsistency between `SymfonyStyle::askQuestion(new ChoiceQuestion(...))` and `SymfonyStyle::choice(...)`, the former does not support to have a choice value as default instead of a choice key while the latter handles both.
This is causing an `undefined index` notice breaking interactive command testing, fixed here.

Commits
-------

c51dda0 [Console] Fix SymfonyQuestionHelper::askQuestion() with choice value as default

@chalasr chalasr closed this Sep 8, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.