Blog

Les tests unitaires sous Magento 1.x – Partie 1

Cette première partie explique la mise en place d’une boutique Magento avec Composer et Modman, les tests unitaires sous Magento à l’aide du module EcomDev_PHPunit, ainsi nous espérons qu’il permettra à d’autres personnes de tester leurs modules. Vous pouvez récupérer le code du projet à l’adresse suivante Magento Unit Tests

Magento est une solution e-commerce très complète et flexible, mais qui vient avec un léger inconvénient, la difficulté de base d’écrire et exécuter des tests unitaires.

Or chez Occitech, le TDD nous tient à cœur, et nous avons cherché comment écrire des tests unitaires pour Magento.

La communauté active de Magento nous offre une extension Magento pour notre plus grand plaisir : EcomDev_PHPunit. Cette extension permet ni plus ni moins de tester votre boutique dans les moindre recoins.

Installation du module

Hélas à l’heure où nous écrivons ces lignes, l’installation du module n’est pas aussi facile que le laisse penser la documentation.

Notre cas de test sera une boutique Magento 1.9.1.0 fraîchement installée via Modman et Composer.

Installation de la boutique :

Le composer.json qui va bien :

Et installons le tout : composer install.

Paramétrage Ecomdev_PHPUnit :

Une fois internet téléchargé, il vous faudra faire plusieurs modifications :

Copier-coller le fichier local.xml.phpunit et phpunit.xml.dist au bon endroit :

Créons une base de données de test : /!\ Attention ! Il faut que la base de données de notre Magento fraîchement installée soit en place

Exécutons le shell Ecomdev :

— Et enfin exécutons les tests une première fois pour mettre en place la base de test (l’exécution peut prendre un peu de temps) :

Vous devriez avoir en sortie quelque chose comme :

Maintenant passons aux choses sérieuses :

Les tests

Écrivons notre 1er test, pour ce faire nous allons créer un premier module que l’on nommera Occitech_UnitTests. Créons le répertoire pour notre module mkdir -p .modman/Occitech_UnitTests.

Ajoutons également un fichier nommé .basedir pour éviter de répeter le répertoire de destination dans les mappings modman : echo "htdocs/" > .modman/.basedir.

Créons la structure du module :

Contenu du fichier Occitech_UnitTests.xml :

Contenu du fichier config.xml :

Notez la partie avec le noeud <phpunit> permettant au listener de EcomDev de venir exécuter nos futur tests.

Contenu du fichier .modman :

Éditons le fichier phpunit.xml.dist pour ajouter notre répertoire pour les tests :

Et créeons un test simple pour nous assurer du fonctionnement de la test suite.

Contenu du fichier Observer.php :

Faisons un modman deploy afin de placer nos nouveaux fichiers : vendor/bin/modman deploy-all et exécutons nos tests vendor/bin/phpunit --testsuite  "Occitech Test Suite" le résultat devrait être le suivant :

Notre test suite est donc opérationnelle.

Nous allons donc maintenant écrire un test pour définir quel événement écoutera notre observer, dans notre cas nous allons écouter l’événement qui est émis lors de l’ajout d’un produit au panier à savoir checkout_cart_add_product_complete

Voici le test qui va couvrir cette définition :

On ré-exécute nos tests afin de s’assurer que notre test échoue vendor/bin/phpunit --testsuite  "Occitech Test Suite".

Puis on va donc modifier notre fichier config.xml et on va créer notre observer.

Le contenu de notre fichier Observer.php :

Redéployons nos fichiers, et exécutons à nouveaux tests, et la notre test passe au vert.

Maintenant nous allons tester que notre méthode onProductAdded fasse une action bien déterminée.

Pour ce faire on va créer des fixtures toutes simples default.yaml

Que l’on va mettre dans un répertoire fixtures

Nous allons ensuite rajouter une annotation dans notre classe de test afin de charger la fixture créer pour tous les tests.

La fonctionnalité que l’on voudra tester est la suivante :

Lorsqu’un utilisateur ajoutera un produit particulier au panier, automatiquement il aura un produit offert ajouté également.

Ci-dessous le test (basique) de la fonctionnalité :

Puis exécutons les tests pour vérifier que notre dernier test échoue.

Expliquons brièvement le contenu du test :

On génère un « faux » évènement qui sera utilisé par notre observeur.

Puis on va créer un Mock du model Checkout/Cart afin de tester que l’on ajoute bien au panier le produit offert.

Ne surtout pas oublier l’utilisation de $this->replaceByMock qui a pour effet de ne pas instancier le vrai model Checkout/Cart dans la méthode testée de notre observer mais de récupérer le mock précédemment défini.

Il ne nous reste plus qu’a écrire le code faisant passer ce test :

Ré-exécutons nos tests, et là vérifions que tous nos tests sont aux verts.

Bon, le problème c’est que l’argent c’est le nerf de la guerre, et que l’on veut offir ce cadeau quand dans le cas ou le produit ajouté est un produit spécifique (tant qu’à faire sur lequel on marge le plus) et éviter d’offire un produit cadeau quand on rajoute ce produit cadeau.

Nous allons écrire un test qui dans notre cas va simplement vérifier que lors de l’ajout du produit cadeau lui même, on ne rajoutera pas le cadeau.

Puis exécutons les tests pour vérifier que notre dernier test échoue.

Enfin rajoutons le code dans notre observer, permettant de faire passer ce test.

Puis exécutons à nouveaux les tests, et encore une fois ils sont verts.

La partie 1 sur les tests touchent presque à sa fin, mais avant de partir, refactorons nos tests car comme on peut le voir il y a beaucoup de similarités entre les tests.

La classe de test une fois refactorée :

Ce qui a été fait:

  • On a extrait la génération du faux évènement
  • On a également extrait la portion de code générant le mock du model Checkout/Cart
  • On a crée un attribut privé ayant une instance de notre observer pour éviter de rédéclarer le Mage::getModel('occitech_unittests/observe') pour chaque test.

Toujours en vérifiant entre chaque étape que nos tests soient verts

Maintenant la partie 1 touche à sa fin.

Ce qui est prévu pour la partie 2, l’utilisation des dataProviders et expectations dans nos tests, ainsi que le test des blocks et controlleurs.