zaseknutí testu Kdyby/Console commandu
- Petr Parolek
- Člen | 455
Ahoj,
už si nevím rady s testem:
<?php
namespace Tests\App\Presenters;
require __DIR__ . '/../bootstrap.php';
use App\Commands\AddUserCommand;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Tester\CommandTester;
class AddUserCommandTest extends \Tester\TestCase
{
use \Testbench\TCompiledContainer;
use \Testbench\TDoctrine;
public function testExecute()
{
$console = $this->getService(\Symfony\Component\Console\Application::class);
$command = $console->find('app:add-user');
$commandTester = new CommandTester($command);
// Equals to a user inputting "This", "That" and hitting ENTER
// This can be used for answering two separated questions for instance
$commandTester->setInputs([
'testxxx',
'testxxx@example.com',
'Test',
'Xxx',
'test role',
'1',
'password',
'password',
]);
$commandTester->execute([]);
$output = $commandTester->getDisplay();
\Tester\Assert::match('#was successfully created#i', $output);
}
}
(new AddUserCommandTest)->run();
<?php
namespace App\Commands;
use App\Model\Repositories\UsersRepository;
use App\Model\Repositories\UserRolesRepository;
use App\Model\Services\UserService;
use Nette\Utils\Validators;
use Nette\Utils\Strings;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
class AddUserCommand extends Command
{
/** @var UsersRepository */
private $usersRepository;
/** @var UserRolesRepository */
private $userRolesRepository;
/** @var UserService */
private $userService;
public function __construct(UsersRepository $usersRepository, UserRolesRepository $userRolesRepository, UserService $userService)
{
parent::__construct();
$this->usersRepository = $usersRepository;
$this->userRolesRepository = $userRolesRepository;
$this->userService = $userService;
}
protected function configure()
{
$this->setName('app:add-user')
->setDescription('Add new user');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
/* @var $questionHelper QuestionHelper */
$questionHelper = $this->getHelper('question');
$usersRepository = $this->usersRepository;
$usernameQuestion = new Question('Userame: ');
$usernameQuestion->setValidator(function ($value) use ($usersRepository) {
if (trim($value) === '') {
throw new \Exception('Username can not be empty');
}
if (Strings::webalize($value) != $value) {
throw new \Exception('Username contains unexcepted characters');
}
$username = $usersRepository->findBy(['username' => $value]);
if ($username) {
throw new \Exception('Username is already registered');
}
return $value;
});
$values['username'] = $questionHelper->ask($input, $output, $usernameQuestion);
$emailQuestion = new Question('E-mail: ');
$emailQuestion->setValidator(function ($value) use($usersRepository) {
if (trim($value) === '') {
throw new \Exception('E-mail can not be empty');
}
if (!Validators::isEmail($value)) {
throw new \Exception('E-mail is not valid');
}
$email = $usersRepository->findBy(['email' => $value]);
if ($email) {
throw new \Exception('E-mail address is already registered');
}
return $value;
});
$values['email'] = $questionHelper->ask($input, $output, $emailQuestion);
$firstNaneQuestion = new Question('First name: ');
$firstNaneQuestion->setValidator(function ($value) {
if (trim($value) === '') {
throw new \Exception('First name can not be empty');
}
return $value;
});
$values['firstName'] = $questionHelper->ask($input, $output, $firstNaneQuestion);
$lastNaneQuestion = new Question('Last name: ');
$lastNaneQuestion->setValidator(function ($value) {
if (trim($value) === '') {
throw new \Exception('Last name can not be empty');
}
return $value;
});
$values['lastName'] = $questionHelper->ask($input, $output, $lastNaneQuestion);
$companRoleQuestion = new Question('Company role: ');
$companRoleQuestion->setValidator(function ($value) {
if (trim($value) === '') {
throw new \Exception('Companz role can not be empty');
}
return $value;
});
$values['companyRole'] = $questionHelper->ask($input, $output, $companRoleQuestion);
$values['recoveryToken'] = null;
$userRoles = $this->userRolesRepository->findPairs('name');
$userRoleQuestion = new ChoiceQuestion(
'Please select user role',
$userRoles
);
$userRoleQuestion->setErrorMessage('User role %s is invalid.');
$userRole = $questionHelper->ask($input, $output, $userRoleQuestion);
//dump($userRole);
$values['userRole'] = $this->userRolesRepository->findOneBy(['name' => $userRole]);
$passwordQuestion = new Question('Password: ');
$passwordQuestion->setValidator(function ($value) {
if (trim($value) === '') {
dump($value);
throw new \Exception('The password can not be empty');
}
return $value;
});
$passwordQuestion->setHidden(TRUE);
$passwordQuestion->setHiddenFallback(FALSE);
$values['password'] = $questionHelper->ask($input, $output, $passwordQuestion);
$password = $values['password'];
$confirmPasswordQuestion = new Question('Confirm password: ');
$confirmPasswordQuestion->setValidator(function ($value) use ($password) {
if ($value != $password) {
throw new \Exception("The password doesn't match");
}
return $value;
});
$confirmPasswordQuestion->setHidden(TRUE);
$confirmPasswordQuestion->setHiddenFallback(FALSE);
$questionHelper->ask($input, $output, $confirmPasswordQuestion);
$values = \Nette\Utils\ArrayHash::from($values);
$user = $this->userService->insertUser($values);
$output->writeln('User ' . $user->getUsername() . ' was successfully created!');
}
}
Když zakomentuju:
$passwordQuestion->setHidden(TRUE);
$passwordQuestion->setHiddenFallback(FALSE);
nebo
$passwordQuestion->setValidator(function ($value) {
if (trim($value) === '') {
dump($value);
throw new \Exception('The password can not be empty');
}
return $value;
});
tak test proběhne ok, jinak se zasekne a nic netestuje. Prosím, kde je chyba?
- jiri.pudil
- Nette Blogger | 1032
Problém je v tom, že pokud nemá QuestionHelper k dispozici stty, ptá se uživatele na hidden response spuštěním systémové (nebo na Windows dokonce vlastní) binárky, která prostě čeká na uživatelský vstup, test netest. No a pokud test spustíš přes runner Nette Testeru, běží v podprocesu mimo terminál, kde stty selže. Pokud test pustíš samostatně napřímo přes php, projde. Taky jsem na to narazil a zatím jsem to nedokázal vyřešit.
- jiri.pudil
- Nette Blogger | 1032
A v testu mám
\exec('stty 2>&1', $output, $exitCode);
if ($exitCode !== 0) {
\Tester\Environment::skip('Cannot read hidden response if not running in a terminal with stty');
}
takže se v případě nepříznivého prostředí raději přeskočí, než aby čekal na vstup, který nikdy nepřijde.
- Petr Parolek
- Člen | 455
OK – vyřešeno workaroundem – nápad borce ze symfony eng slacku:
ve třídě commandu přidat:
private $disableHiddenQuestion = false;
public function disableHiddenQuestion($disable = true)
{
$this->disableHiddenQuestion = $disable;
}
...
if (false === $this->disableHiddenQuestion) {
$passwordQuestion->setHidden(true);
$passwordQuestion->setHiddenFallback(true);
}
a do testu přidat:
$command->disableHiddenQuestion();