diff --git a/fr/appendices/3-5-migration-guide.rst b/fr/appendices/3-5-migration-guide.rst index 882e95564d..7598b2e873 100644 --- a/fr/appendices/3-5-migration-guide.rst +++ b/fr/appendices/3-5-migration-guide.rst @@ -136,6 +136,17 @@ variations mineures qui pourraient avoir un impact sur votre application. Nouvelles Fonctionnalités ========================= +Nouveau Lanceur de Console +-------------------------- + +3.5.0 ajoute ``Cake\Console\CommandRunner``. Cette classe, avec ``Cake\Console\CommandCollection``, +intègre l'environnement CLI dans la nouvelle classe ``Application``. +Les classes ``Application`` peuvent maintenant implémenter un hook ``console()`` +qui permet d'avoir un contrôle complet sur les commandes CLI exposées, comment +elles sont nommées et comment les shells récupèrent leurs dépendances. Adopter +cette nouvelle classe nécessite que vous remplaciez le contenu de votre fichier +``bin/cake.php`` par le `fichier suivant `_. + Cache ----- diff --git a/fr/console-and-shells.rst b/fr/console-and-shells.rst index 619ae84ac7..2eba25b202 100644 --- a/fr/console-and-shells.rst +++ b/fr/console-and-shells.rst @@ -1,5 +1,5 @@ -Shells, Tasks & Outils de Console -################################# +Outils de Console, Shells, & Tasks +################################## .. php:namespace:: Cake\Console @@ -53,7 +53,7 @@ avec un exécutable dans le répertoire **bin**:: Quand vous lancez la Console sans argument, cela affiche ce message d'aide:: - Welcome to CakePHP v3.0.0 Console + Welcome to CakePHP v3.5.0 Console --------------------------------------------------------------- App : App Path: /Users/markstory/Sites/cakephp-app/src/ @@ -139,11 +139,6 @@ application, lancez:: Vous devriez voir la sortie suivante:: - Welcome to CakePHP Console - --------------------------------------------------------------- - App : app - Path: /Users/markstory/Sites/cake_dev/src/ - --------------------------------------------------------------- Hello world. Comme mentionné précédemment, la méthode ``main()`` dans les shells est une @@ -190,42 +185,6 @@ de position ou l'option est interprété en tant que nom de commande. Si vous voulez utiliser des arguments et des options, vous devriez utiliser un autre nom de méthode que ``main``. -Utiliser les Models dans vos shells ------------------------------------ - -Vous avez souvent besoin d'accéder à la logique métier de votre application -dans les utilitaires de shell. CakePHP rend cela super facile. Vous pouvez -charger les models dans les shells, juste comme vous le feriez dans un -controller en utilisant ``loadModel()``. Les models définis sont chargés en -propriétés attachées à votre shell:: - - namespace App\Shell; - - use Cake\Console\Shell; - - class UserShell extends Shell - { - - public function initialize() - { - parent::initialize(); - $this->loadModel('Users'); - } - - public function show() - { - if (empty($this->args[0])) { - // Utilisez error() avant CakePHP 3.2 - return $this->abort("Merci de rentrer un nom d'utilisateur."); - } - $user = $this->Users->findByUsername($this->args[0])->first(); - $this->out(print_r($user, true)); - } - } - -Le shell ci-dessus récupérera un utilisateur par son username et affichera -l'information stockée dans la base de données. - Les Tâches Shell ================ @@ -319,6 +278,42 @@ Chargera et retournera une instance ProjectTask. Vous pouvez charger les tâches $progressBar = $this->Tasks->load('ProgressBar.ProgressBar'); +Utiliser les Models dans vos shells +----------------------------------- + +Vous avez souvent besoin d'accéder à la logique métier de votre application +dans les utilitaires de shell. CakePHP rend cela super facile. Vous pouvez +charger les models dans les shells, juste comme vous le feriez dans un +controller en utilisant ``loadModel()``. Les models définis sont chargés en +propriétés attachées à votre shell:: + + namespace App\Shell; + + use Cake\Console\Shell; + + class UserShell extends Shell + { + + public function initialize() + { + parent::initialize(); + $this->loadModel('Users'); + } + + public function show() + { + if (empty($this->args[0])) { + // Utilisez error() avant CakePHP 3.2 + return $this->abort("Merci de rentrer un nom d'utilisateur."); + } + $user = $this->Users->findByUsername($this->args[0])->first(); + $this->out(print_r($user, true)); + } + } + +Le shell ci-dessus récupérera un utilisateur par son username et affichera +l'information stockée dans la base de données. + Shell Helpers ============= @@ -1265,6 +1260,42 @@ un exemple de documentation: +Renommer des Commandes +====================== + +Par défaut, CakePHP va automatiquement chercher et mettre à disposition +toutes les commandes dans votre application et ses plugins. Il est possible +que vous souhaitiez réduire le nombre de commandes exposées lorsque vous +construisez une application console indépendante. Pour cela, vous pouvez utiliser +le hook ``console()`` de votre Application pour limiter le nombre de commandes +qui sont exposées:: + + namespace App; + + use App\Shell\UserShell; + use App\Shell\VersionShell; + use Cake\Http\BaseApplication; + + class Application extends BaseApplication + { + public function console($commands) + { + // Ajout par nom de de classe + $commands->add('user', UserShell::class); + + // Ajout par une instance + $commands->add('version', new VersionShell()); + + return $commands; + } + } + +Dans l'exemple ci-dessus, les seules commandes qui seront disponibles seront +``help``, ``version`` and ``user``. + +.. versionadded:: 3.5.0 + Le hook ``console`` a été ajouté. + Routing dans shells / CLI ========================= diff --git a/fr/development/testing.rst b/fr/development/testing.rst index 27ceff5e6d..dfcf81c3b2 100755 --- a/fr/development/testing.rst +++ b/fr/development/testing.rst @@ -1464,6 +1464,287 @@ activé. Vous pouvez utiliser ceci afin que votre test marche dans les deux cas: json_encode($data, Configure::read('debug') ? JSON_PRETTY_PRINT : 0); +Tests d'Intégration de la Console +================================= + +Pour faciliter les tests de vos applications console, CakePHP est doté d'une classe +``ConsoleIntegrationTestCase`` qui peut être utilisée pour tester vos applications +consoles et faire des assertions sur leurs résultats. + +.. versionadded:: 3.5.0 + + ``ConsoleIntegrationTestCase`` a été ajoutée dans 3.5.0. + +Pour commencer à tester votre application console, créez un "test case" qui *extends* +``Cake\TestSuite\ConsoleIntegrationTestCase``. Cette classe contient une méthode +``exec()`` qui est utilisée pour exécuter votre commande. Vous pouvez passer la +même chaîne que vous passeriez au CLI dans cette méthode. + +Commençons par créer un shell très simple, stocké dans **src/Shell/MyConsoleShell.php**:: + + namespace App\Shell; + + use Cake\Console\ConsoleOptionParser; + use Cake\Console\Shell; + + class MyConsoleShell extends Shell + { + public function getOptionParser() + { + $parser = new ConsoleOptionParser(); + $parser->setDescription('My cool console app'); + + return $parser; + } + } + +Pour écrire un test d'intégration pour ce shell, on va créer un "test case" dans +**tests/TestCase/Shell/MyConsoleShellTest.php** qui *extends* +``Cake\TestSuite\ConsoleIntegrationTestCase``. Ce shell ne fait pas grand chose +pour le moment, mais testons que la description de notre shell est affichée dans +``stdout``:: + + namespace App\Test\TestCase\Shell; + + use Cake\TestSuite\ConsoleIntegrationTestCase; + + class MyConsoleShellTest extends ConsoleIntegrationTestCase + { + public function testDescriptionOutput() + { + $this->exec('my_console'); + $this->assertOutputContains('My cool console app'); + } + } + +Les tests passent ! Même si c'est un exemple très simple, cela prouve que construire +un test d'intégration pour une application console est facile. Continuons en ajoutant +des sous-commandes et des options à notre shell:: + + namespace App\Shell; + + use Cake\Console\ConsoleOptionParser; + use Cake\I18n\FrozenTime; + + class MyConsoleShell extends Shell + { + public function getOptionParser() + { + $parser = new ConsoleOptionParser(); + + $updateModifiedParser = new ConsoleOptionParser(); + $updateModifiedParser->addArgument('table', [ + 'help' => 'Table to update', + 'required' => true + ]); + + $parser + ->setDescription('My cool console app') + ->addSubcommand('updateModified', [ + 'parser' => $updateModifiedParser + ]); + + return $parser; + } + + public function updateModified() + { + $table = $this->args[0]; + $this->loadModel($table); + $this->{$table}->query() + ->update() + ->set([ + 'modified' => new FrozenTime() + ]) + ->execute(); + } + } + +C'est maintenant un shell plus complexe avec une sous-commande et son propre parser. +Testons la sous-commande ``updateModified``. Modifier votre "test case" avec le +morceau de code suivant:: + + namespace Cake\Test\TestCase\Shell; + + use Cake\Console\Shell; + use Cake\I18n\FrozenTime; + use Cake\ORM\TableRegistry; + use Cake\TestSuite\ConsoleIntegrationTestCase; + + class MyConsoleShellTest extends ConsoleIntegrationTestCase + { + + public $fixtures = [ + // assumes you have a UsersFixture + 'app.users' + ]; + + public function testDescriptionOutput() + { + $this->exec('my_console'); + $this->assertOutputContains('My cool console app'); + } + + public function testUpdateModified() + { + $now = new FrozenTime('2017-01-01 00:00:00'); + FrozenTime::setTestNow($now); + + $this->loadFixtures('Users'); + + $this->exec('my_console update_modified Users'); + $this->assertExitCode(Shell::CODE_SUCCESS); + + $user = TableRegistry::get('Users')->get(1); + $this->assertSame($user->modified->timestamp, $now->timestamp); + + FrozenTime::setTestNow(null); + } + } + +Comme vous pouvez le voir via la méthode ``testUpdateModified``, nous testons +que la sous-commande ``update_modified`` met à jour la table que nous passons +comme premier argument. Premièrement, nous faisons l'assertion que le shell +a terminé de s'exécuter avec le bon code de statut, ``0``. Ensuite, nous testons +que notre sous-commande a fait son travail, c'est-à-dire qu'elle a correctement +mis à jour la colonne ``modified`` de la table que nous avons passée en argument. + +Gardez bien en mémoire que la méthode ``exec()`` accepte la même chaîne que ce +que vous tapez dans votre CLI, donc vous pouvez ajouter des options et des arguments +pour tester un maximum de cas. + +Tester les Shells Interactifs +----------------------------- + +Les applications console sont souvent interactives. Tester les shells interactifs +avec la classe ``Cake\TestSuite\ConsoleIntegrationTestCase`` va seulement nécessiter +que vous passiez les données attendues comme second paramètre de la méthode ``exec()``. +Ces données doivent être passées sous forme de tableau, dans l'ordre dans lequel ces +données sont attendues. + +En continuant avec notre shell d'exemple, ajoutons une sous-commande interactive. +Mettez à jour la classe de shell avec le code suivant:: + + namespace App\Shell; + + use Cake\Console\ConsoleOptionParser; + use Cake\Console\Shell; + use Cake\I18n\FrozenTime; + + class MyConsoleShell extends Shell + { + public function getOptionParser() + { + $parser = new ConsoleOptionParser(); + + $updateModifiedParser = new ConsoleOptionParser(); + $updateModifiedParser->addArgument('table', [ + 'help' => 'Table to update', + 'required' => true + ]); + + $parser + ->setDescription('My cool console app') + ->addSubcommand('updateModified', [ + 'parser' => $updateModifiedParser + ]) + // add a new subcommand + ->addSubcommand('bestFramework'); + + return $parser; + } + + public function updateModified() + { + $table = $this->args[0]; + $this->loadModel($table); + $this->{$table}->query() + ->update() + ->set([ + 'modified' => new FrozenTime() + ]) + ->execute(); + } + + // create this interactive subcommand + public function bestFramework() + { + $this->out('Hi there!'); + + $framework = $this->in('What is the best PHP framework?'); + if ($framework !== 'CakePHP') { + $this->err("I disagree that '$framework' is the best."); + $this->_stop(Shell::CODE_ERROR); + } + + $this->out('I agree!'); + } + } + +Maintenant que nous avons une sous-commande interactive, nous pouvons ajouter +un test qui va permettre de vérifier que nous recevons la réponse attendue et +un test où nous passerons une réponse que nous savons incorrecte. Ajoutez les +méthodes suivantes dans **tests/TestCase/Shell/MyConsoleShellTest.php**:: + + public function testBestFramework() + { + $this->exec('my_console best_framework', [ + 'CakePHP' + ]); + $this->assertExitCode(Shell::CODE_SUCCESS); + $this->assertOutputContains('I agree!'); + } + + public function testBestFrameworkWrongAnswer() + { + $this->exec('my_console best_framework', [ + 'my homemade framework' + ]); + $this->assertExitCode(Shell::CODE_ERROR); + $this->assertErrorRegExp("/I disagree that \'(.+)\' is the best\./"); + } + +Comme vous pouvez le voir dans ``testBestFramework``, il répond à la première +valeur passée "CakePHP". Puisque que c'est la réponse attendue par notre +sous-commande, le shell termine avec un succès après avoir retourné une réponse. + +Le second test, ``testBestFrameworkWrongAnswer``, passe une réponse invalide ce +qui fait que notre shell échoue et retourne le code ``1``. Nous faisons également +l'assertion que ``stderr`` a bien reçu notre erreur et que le retour contient bien +la valeur incorrecte passée en paramètre. + +Tester le CommandRunner +----------------------- + +Pour tester les shells qui sont "dispatchées" via la classe ``CommandRunner``, +activer le dans votre "test case" avec la méthode suivante:: + + $this->useCommandRunner(); + +.. versionadded:: 3.5.0 + + La classe ``CommandRunner`` a été ajoutée dans 3.5.0. + +Méthodes d'assertions +--------------------- + +La classe ``Cake\TestSuite\ConsoleIntegrationTestCase`` met à disposition +plusieurs méthodes qui facilitent les assertions des sorties de console:: + + // s'assure que le shell a quitté avec le code attendu + $this->assertExitCode($expected); + + // s'assure que stdout contient une chaîne + $this->assertOutputContains($expected); + + // s'assure que stderr contient une chaîne + $this->assertErrorContains($expected); + + // s'assure que stdout "match" une regex + $this->assertOutputRegExp($expected); + + // s'assure que stderr "match" une regex + $this->assertErrorRegExp($expected); Tester les Views ================