Bonus : Les tests fonctionnels en PHP
Utiliser Behat
Installation
Behat peut être installé par le biais de composer.
Créez un dossier test-behat, puis placez-vous dans ce dossier.
Ensuite, installez behat :
Tester une implémentation simple
Pour cet exercice guidé je reprend et adapte l'exemple de la documentation de Behat.
Behat : créer une feature
Behat fonctionne en décrivant des fonctionnalités (features) dans un langage simpliste mais lisible par des profils pas exclusivement tech. Ce langage simpliste, qui n'est pas dépendant du langage d'implémentation est appelé Gherkin.
Rédiger une feature
Notre feature va s'assurer que pour pouvoir choisir les produits qu'iel souhaite acheter notre user va pouvoir les ajouter à un panier.
Avec Gherkin on pourait l'exprimer comme suit :
Nous avons ensuite quelques règles à préciser :
la TVA est de 20%
Les frais de livraison des paniers supérieurs ou égaux à 10
Les frais de livraisons des paniers inférieurs à 10
Toujours en Gherkin, nous allons pouvoir l'écrire comme suit :
Behat : les scénarios de test
Pour préciser le comportement que vous souhaitez voir implémenté, vous allez rédiger des scénarios de tests :
Si j'ajoute un produit unique qui vaut moins de 10$
Si j'ajoute 2 produits uniques qui valent chacun moins de 10$
Si j'ajoute un produit unique qui vaut plus de 10$
Si j'ajoute plusieurs produits qui valent chacun plus de 10$
En Gherkin :
Behat : suite des scénarios
Vous avez votre première feature, qui a pu être défini avec des profils non techniques (chefs de projets, stratégistes commerciaux, etc etc). Vous avez donc des spécifications.
Commençons par stocker notre feature dans un fichier.
Créez un dossier features à la racine de votre projet puis stockez votre feature dans un fichier features/basket.feature.
Behat : tester une feature
Nous allons nous placer dans le cas de figure où vous créez une nouvelle code base de toute pièce, aucun code existant sur cette fonctionnalité, vous démarrez from scratch.
Initialiser la suite de tests
Behat devrait vous avoir créé un dossier features/bootstrap et un fichier features/bootstrap/FeatureContext.php.
Lancer les tests
Behat devrait reconnaitre que vous avez 4 scénarios, tous indéfinis et 20 étapes, toutes indéfinies. Chez moi la fin de l'affichage terminal ressemble à ceci :
Il propose ensuite de prégénérer mon contexte avec les étapes manquantes. Dites lui que vous souhaitez utiliser le contexte FeatureContext (option 1 chez moi).
Copiez ensuite le code qu'il vous propose dans votre classe FeatureContext (effacez le constructeur de la classe).
FeatureContext.php
Votre classe FeatureContext devrait ressembler à ceci :
features/bootstrap/FeatureContext.php
FeatureContext.php suite
Vous avez maintenant les étapes de votre test définies. Si vous voulez éviter de devoir les recopier à la main dans votre fichier vous pouvez utiliser :
Vérifiez que le code généré fait bien un use des PendingException. Si ça n'est pas le cas, ajoutez la ligne suivante :
Test Driven Development
Dans une approche TDD la rédaction du code est un peu différente d'une approche classique, en effet, nous rédigeons des tests puis seulement ensuite nous rédigeons du code pour satisfaire ces tests.
Nous allons donc commencer par rédiger les tests de notre classe FeatureContext sans nous soucier de ce qui a ou non (non dans notre cas) été implémenté.
Le constructeur de FeatureContext
Commençons par la base : nous devrions avoir un catalogue de produits et un panier :
features/bootstrap/FeatureContext.php
FeatureContext : prix des produits
Notre catalogue devrait contenir des produits, qui ont des prix.
Nous allons transformer le très générique :
en
FeatureContext : ajouter un produit au panier
devient
FeatureContext : vérifier le nombre de produits dans le panier
devient
FeatureContext : vérifier le prix du panier
devient
FeatureContext : seconde vérification du nombre de produits
Nous supprimons
qui est devenu un doublon.
Ajouter PHPUnit
Pour vérifier que quelque chose est vrai, nous allons utiliser l'outil d'assertion de PHPUnit (Behat n'en propose pas). Installons donc PHPUnit :
Implémentation
Nous allons laisser Behat nous guider pour l'implémentation de notre solution en PHP.
Commençons par le faire tourner :
Il nous renvoie des erreurs et c'est normal, nous appelons tout un tas de choses qui n'existent pas encore.
Implémentation : créer les fichiers des classes
Créez un dossier src dans lequel vous allez créer deux fichiers :
src/Catalogue.php
src/Basket.php
récupérez ensuite ces classes dans votre features/bootstrap/FeatureContext.php:
Et faites tourner Behat :
Cette fois il nous signale que les scénarios échouent parce que les méthodes appelées sur nos classes sont manquantes.
Contenus des classes : Catalogue
Dans nos test, nous appelons la méthode Catalogue::setProductPrice(string $product, float $price).
Nous allons donc la créer.
src/Catalogue.php
Nous allons ajouter un attribut à notre classe qui sera un tableau associatif sous la forme $prices[$product] = $price.
src/Catalogue.php
et nous allons ensuite compléter notre méthode pour qu'elle ajoute à un produit et son prix à notre tableau :
src/Catalogue.php
et au passage nous allons ajouter un getter pour nos prix :
src/Catalogue.php
Contenus des classes : Basket
Dans notre test, nous appelons la méthode Basket::addProduct(string $product), nous appelons aussi la méthode Basket::getTotalPrice() et nous appelons une assertion (assertCount) qui nous dit implicitement que notre classe doit être Countable.
Un Basket Countable
En PHP pour pouvoir utiliser count sur une classe celle-ci doit implémenter l'interface Countable, nous allons donc transformer notre classe Basket:
src/Basket.php
Ajouter un produit
Pour pouvoir ajouter un produit nous allons allons avoir besoin d'une liste de produits. Nous savons que nous ajoutons un produit par son nom, mais nous savons aussi que nous allons avoir besoin de son prix. Il pourrait donc être utile d'avoir le catalogue en référence dans notre classe pour faire le lien entre le nom du produit et son prix.
src/Basket.php
Basket : suite
ensuite nous allons créer notre méthode addProduct(string $product):
src/Basket.php
Nous pouvons aussi mettre à jour notre méthode Basket::count()
src/Basket.php
Basket : suite
Récupérer le prix total du panier
Nous savons que le prix total de notre panier est équivalent au prix de nos produits + la livraison + la TVA (dans la plupart des pays dont la France la TVA d'une livraison est la même que celle des produits livrés).
src/Basket.php
Basket : suite
Appel au constructeur
Nous avons ajouté un argument au constructeur de la classe Basket, n'oubliez pas de répercuter ce changement dans votre FeatureContext:
features/bootstrap/FeatureContext.php
Tester
Nous avons normalement implémenté tout ce dont nous avions besoin.
Faites une nouvelle fois tourner Behat :
Tous vos tests devraient cette fois être validés :)