diff --git a/BDD_CORRIGE.md b/BDD_CORRIGE.md new file mode 100644 index 0000000000000000000000000000000000000000..c14108abf7a7b85698114b00bbe9214abea2b242 --- /dev/null +++ b/BDD_CORRIGE.md @@ -0,0 +1,428 @@ +# Corrigé du TP BDD + +## Installation des logiciels + +Récupération du projet sur le dépôt : +```bash +git clone https://gitlab.univ-lorraine.fr/pierron9/pastebin +cd pastebin +``` + +Création de la branche d etravauil : + +```bash +git branch pierron9 +git switch pierron9 +git status +git push --set-upstream origin pierron9 +``` + +Mise en route et vérification du fonctionnement de l'application : + +```bash +composer self-update +composer update +php -S lacalhost:8000 +``` + +Installation de *codeception* : + +```bash +composer remove phpunit/phpunit +composer require "codeception/codeception" --dev +composer update +php vendor/bin/codecept bootstrap +``` + +## Premiers tests avec `PhpBrowser` + +Afin de visualiser les mêmes informations en **anglais** que ce que vous fournit le module `PhpBrowser` de navigation *headless* de *codeception* , je vous recommande d'utiliser un navigator comme `Firefox`, que vous configurerez pour afficher les pages en anglais. + +Configuration du serveur pour les tests d'acceptation dans `tests/Acceptance.suite.yml` : + +```yaml + actor: AcceptanceTester + modules: + enabled: + - PhpBrowser: + url: http://localhost:8000/ +``` + +Création du premier test dans `tests/Acceptance/PremierCest.php`, attention par défaut la page est en anglais. +Si vous avez une erreur, vous pouvez consulter le contenu du fichier `HTML` dans `tests/_output`. + +Voici un exemple de premier test, on contrôle la présence de la petite *maxime* en bas de page *Because ignorance is bliss* : + +```php +<?php + +namespace Tests\Acceptance; + +use Tests\Support\AcceptanceTester; + +class PremierCest +{ + // tests + public function frontpageWorks(AcceptanceTester $I) + { + $I->amOnPage('/'); + $I->see('Because ignorance is bliss'); + } +} +``` + + +## Tester une application avec JS côté client + +Malheureusement le logiciel `PrivateBin` nécessite un navigateur capable d'interpréter le code `Javascript` embarqué. + +Avec `codeception`, la solution passe par le module `WebDriver`, qu'il faut tout d'abord installer. +Ce module permettra d'utiliser l'un des navigateurs visuels suivants : `Chrome`, `Firefox`, +ou l'un des navigateurs *headless* suivants : `Selenium Headless`, `ChromeDriver`, `GeckoDriver` + +Installation de `WebDriver` et de `Selenium` dans une nouvelle branche `git` nommée `<votre login>_sel` : +```bash +git branch -C <votre login>_sel +git push --set-upstream origin pierron9_sel +composer require --dev codeception/module-webdriver +npm install selenium-standalone -g +selenium-standalone install && selenium-standalone start +``` + +### Paramétrage de `Acceptance.suite.yml` + +On va paramétrer nos tests pour utiliser `Google Chrome` en mode *headless* avec le français comme langue souhaitée. +Ce qui donne : +```yml +actor: AcceptanceTester +modules: + enabled: + - WebDriver: + url: 'http://localhost:8000/' + browser: chrome + capabilities: + chromeOptions: + args: ["--headless", "--disable-gpu", "--lang=fr-fr"] + +``` + +### Réalisation de tests simples dans `FrenchBinCest.php` + +On réalise quelques tests simples pour s'assurer que `PrivateBin` est bien lancé et que la première page +d'enregistrement d'un text fonctionne comme souhaitée : + +```php +<?php + +namespace Tests\Acceptance; + +use Tests\Support\AcceptanceTester; + +class FrenchBinCest +{ + // tests + public function privatebin_is_running(AcceptanceTester $I) + { + $I->amOnPage('/'); + $I->see('PrivateBin'); + $I->see('1.5.1'); + } + + public function frontapge_is_in_french(AcceptanceTester $I) + { + $I->amOnPage('/'); + $I->see('Vivons heureux, vivons cachés'); + } + + public function fr_javascript_is_setup(AcceptanceTester $I) + { + $I->amOnPage('/'); + $I->dontSeeElement('#noscript'); + $I->dontSee('JavaScript est requis pour faire fonctionner PrivateBin. Désolé pour cet inconvénient.', '#noscript'); + } + + public function preview_a_text_message_works(AcceptanceTester $I) + { + $msg = "Ceci n'est pas une pipe."; + $I->amOnPage('/'); + //$I->waitForElement('#message', 5); + $I->fillField('#message', $msg); + //$I->waitForElement('#messagepreview', 5); + $I->click('#messagepreview'); + $I->see($msg, '#prettyprint'); + } +} + +``` + +### Réalisation d'un test fonctionnel/acceptation complexe + +On souhaite réaliser un test, qui met en oeuvre le petit scénario suivant : + +1. création d'un document `pastebin`; +2. enregistrement du document ; +3. récupération de l'url du document ; +4. retour à la page principale de création de document ; +5. affichage du document enregistré. + +Voici un exemple de programmation de ce scénario à ajouter à votre batterie de tests : + +```php +public function create_and_read_a_text_message(AcceptanceTester $I) +{ + // Message to paste in a document + $msg = "Ceci n'est pas une pipe."; + + // 1. & 2. Create and send a paste + $I->amOnPage('/'); + $I->fillField('#message', $msg); + $I->click(' Envoyer '); + + // 3. View the send paste and get its url + $I->see('Votre paste est disponible'); + $paste_url = $I->grabAttributeFrom('#pasteurl', 'href'); + $I->click(['css' => 'img[alt="PrivateBin"][src="img/icon.svg"]']); + + // 4. Go on main page to create a new paste + $I->dontSeeInCurrentUrl('?'); + $I->see('Éditer'); + + // 5. Show the first created paste + $I->amOnUrl($paste_url); + $I->see('Ce document expirera'); + $I->see($msg, '#prettyprint'); +} +``` + +## Réalisation des tests comportementaux (BDD) + +**BDD = BEHAVIOR DRIVEN DEVELOPMENT** + +Les idées derrière le BDD peuvent être résumées en : +- décrire des fonctionnalités dans un scénario avec un texte formel +- utiliser des exemples pour rendre concrètes les sbstractions +- implémenter chaque étape d'un scénario pour la tester +- écrire le vrai code réalisant la fonctionnalité.' + +En écrivant chaque fonctionnalité dans un format de Scénario d'Usage (User STory), +qui est **exécutable autmoatiqument** comme un test nous nous assurons que : +les décideurs, les développeurs et les commerciaux sont dans le même bateau. + +Une histoire peut ressembler à cela : + +``` +We can try to write such simple story: + +As a customer I want to buy several products +I put first product with $600 price to my cart +And then another one with $1000 price +When I go to checkout process +I should see that total number of products I want to buy is 2 +Andmy order amount is $1600 +``` +Que l'on pourrait traduire dans un langage plus formel comme ceci : + +```txt +Feature: checkout process + In order to buy products + As a customer + I want to be able to buy several products + + Scenario: + Given I have product with $600 price in my cart + And I have product with $1000 price + When I go to checkout process + Then I should see that total number of products is 2 + And my order amount is $1600 + +``` + +Certains pronoms, adverbes et verbes sont en fait des mots-clés qui vont se répéter d'un scénarion à l'autre. + +Les auteurs du logiciel de BDD **Cucumber**(https://cucumber.io/) ont défini un langage appelé **Gherkin**(https://cucumber.io/docs/gherkin), +qui est composé de 11 mots-clés, que l'on peut traduire dans de nombreuses langues. + +### Utilisation de `Gherkin` avec `codeception` + +Les fonctionnalités en *langue naturelle* sont décrites dans des fichiers `feature` que `codeception` peut créer pour nous : + +```bash +vendor/bin/codecept g:feature Acceptance create_paste +``` + +On voit que le fichier est créé dans le même dossier que les tests d'accepation (`Accepatnce`). Voici le contenu du ficheir `create_paste.faeture` : + +```text +Feature: create_paste + In order to ... + As a ... + I need to ... + + Scenario: try create_paste +``` + +On peut compléter notre scénario : + +```yml +Feature: create_paste + In order to create a new shared paste + As an ordinary user + I need to enter a text and get an url to sahre + + Scenario: register a text as a shared paste + Given I have the text "Ceci n'est pas une pipe." + When I go to frontpage of PrivateBin + Then I should enter my text in an input text area + And send my text to application + And get an url to share and retriev my text + +``` + +Et maintenant on peut déjà le jouer : + +```bash +vendor/bin/codecept dry-run Acceptance create_paste.feature +vendor/bin/codecept gherkin:snippets Acceptance create_paste.feature +``` + +La seconde ligne, nous donne les tests à ajouter dans le fichier `tests/Support/AcceptanceTester.php`, voici le programme généré : + +```php +<?php + +declare(strict_types=1); + +namespace Tests\Support; + +use Codeception\Attribute\Given; +use Codeception\Attribute\When; +use Codeception\Attribute\Then; + +/** + * Inherited Methods + * @method void wantTo($text) + * @method void wantToTest($text) + * @method void execute($callable) + * @method void expectTo($prediction) + * @method void expect($prediction) + * @method void amGoingTo($argumentation) + * @method void am($role) + * @method void lookForwardTo($achieveValue) + * @method void comment($description) + * @method void pause($vars = []) + * + * @SuppressWarnings(PHPMD) +*/ +class AcceptanceTester extends \Codeception\Actor +{ + use _generated\AcceptanceTesterActions; + + /** + * Define custom actions here + */ + + /** + * @Given I have the text :arg1 + */ + public function iHaveTheText($arg1) + { + throw new \PHPUnit\Framework\IncompleteTestError("Step `I have the text :arg1` is not defined"); + } + + /** + * @When I go to frontpage of PrivateBin + */ + public function iGoToFrontpageOfPrivateBin() + { + throw new \PHPUnit\Framework\IncompleteTestError("Step `I go to frontpage of PrivateBin` is not defined"); + } + + /** + * @Then I should enter my text in an input text area + */ + public function iShouldEnterMyTextInAnInputTextArea() + { + throw new \PHPUnit\Framework\IncompleteTestError("Step `I should enter my text in an input text area` is not defined"); + } + + /** + * @Then send my text to application + */ + public function sendMyTextToApplication() + { + throw new \PHPUnit\Framework\IncompleteTestError("Step `send my text to application` is not defined"); + } + + /** + * @Then get an url to share and retrieve my text + */ + public function getAnUrlToShareAndRetrieveMyText() + { + throw new \PHPUnit\Framework\IncompleteTestError("Step `get an url to share and retrieve my text` is not defined"); + } + +} + +``` + +On peut vérifier, que la liste des tests est reconnue avec la commande : `vendor/bin/codecept gherkin:steps Acceptance` + +On peut aussi lancer un test à blanc, pour constater que tous les tests sont définis : `vendor/bin/codecept dry-run Acceptance create_paste.feature` + +### Ajout du code des tests fonctionnelles + +On crée le corps des différentes fonctions dans le ficheir `AcceptanceTester.php` : + +```php +private $msg = ""; + +/** + * @Given I have the text :arg1 + */ + public function iHaveTheText($arg1) + { + $this->msg = $arg1; + } + +/** + * @When I go to frontpage of PrivateBin + */ + public function iGoToFrontpageOfPrivateBin() + { + $this->amOnPage('/'); + $this->see('Vivons heureux, vivons cachés'); + } + +/** + * @Then I should enter my text in an input text area + */ + public function iShouldEnterMyTextInAnInputTextArea() + { + $this->fillField('#message', $this->msg);; + } + +/** + * @Then send my text to application + */ + public function sendMyTextToApplication() + { + $this->click(' Envoyer '); + $this->see('Votre paste est disponible'); + } + +/** + * @Then get an url to share and retrieve my text + */ + public function getAnUrlToShareAndRetrieveMyText() + { + $paste_url = $this->grabAttributeFrom('#pasteurl', 'href'); + $this->amOnUrl($paste_url); + $this->see('Ce document expirera'); + $this->see($this->msg, '#prettyprint'); + } +``` + +Avant d'exécuter les tests, on validera les modifications avec la commande : `vendor/bin/codecept dry-run Acceptance create_paste.feature` + +Puis on peut lancer les tests : `vendor/bin/codecept run Acceptance create_paste.feature --steps` +