Je vous propose de découvrir comment tester son code au sein d'une application Symfony. L'objectif de cette formation est de découvrir à la fois les techniques mais aussi les objectifs derrières les tests.

Pourquoi tester ?

Les tests remplissent deux objectifs principaux :

  • Vérifier que le code fonctionne bien comme attendu.
  • Simplifier la mise à jour de notre code avec un moyen automatisé de s'assurer que l'on ne casse rien à chaque modification (cela s'avère particulièrement pertinent si on travaille à plusieurs car tout le monde n'est pas forcément au courant du travail des autres)
  • Communiquer sur le fonctionnement du code. Lire les tests peut permettre de comprendre des scénarios d'utilisation.

Comment tester ?

Il existe plusieurs manières de tester notre code/application à l'aide des tests.

Les tests unitaires

Ils permettent de tester une "unité" de code (en générale il s'agit d'une méthode ou d'une fonction) afin de s'assurer qu'un morceau de notre code fonctionne comme attendu. Pour ce type de tests nous allons tester le code en totale isolation par rapport au reste du système. Si une méthode interagit avec un objet on créera un double de cet objet qui permettra de simuler les différents comportements (c’est ce que l’on appelle un mock ou un stub).

Ces tests vont avoir l'avantage d'être simple à écrire et rapide à exécuter mais il ne couvre qu'une partie de notre système. En effet, un composant peut fonctionner de manière isolée mais ne pas fonctionner une fois connecté au reste de l'application. Par exemple, un écouteur d'événement peut fonctionner convenablement mais ne pas être câbler correctement dans notre application (par exemple le service pourrait ne pas être détecté).

Les tests fonctionnels

Les tests fonctionnels vont permettre de tester un code qui interagit avec un élément externe. Par exemple, dans le cas de Symfony si on souhaite tester un repository il va falloir le connecter à une base de données afin de s'assurer qu'il fonctionne convenablement. On ne testera plus alors notre code de manière isolée mais on le testera dans le contexte de notre application. C'est aussi un type de test que l'on utilisera pour les controllers où on essaiera d'envoyer une fausse requête en vérifiant que la réponse renvoyée est bien celle attendue. On ne mockera pas nécessairement les différents services mais on testera l'ensemble de notre système.

Ces tests sont intéressants parce qu'ils permettent de s'assurer que notre code fonctionne lorsqu'il communique avec les différents composants de notre application. En revanche il peut être plus difficile d'identifier une erreur car elle peut provenir de n'importe lequel de ces composants. En général, on réservera l'utilisation de ce type de tests aux controllers et aux repositories.

Les tests end to end

Les tests end to end consistent à tester l'application comme un utilisateur standard. On ne va pas nécessairement chercher à tester du code mais plutôt notre interface dans sa globalité. Ce sont les tests les plus complexes à mettre en place parce qu'il faudra piloter un navigateur afin d'automatiser nos scénarios de test. Il existe différentes librairies et outils qui permettent de faire cela mais ces tests vont en général prendre plus de temps car il faut naviguer sur l'application complète. De la même manière il faudra mettre en place un système qui va permettre de réinitialiser les données afin que chaque test soit fait sur un environnement complètement vierge.

Ces tests permettent de voir facilement si notre application contient un bug mais ne permettent pas d'identifier rapidement la cause exacte du problème. Si par exemple un formulaire ne peut pas être soumis correctement il est difficile de savoir si le problème vient de l'interface ou si cela vient d'un problème côté serveur ou encore un problème d'un service tiers. Ces types permettent d'avoir un aperçu général du bon fonctionnement de l'application.

Quand tester ?

Maintenant on peut se demander à quelle étape du projet on va écrire les tests. Est-ce qu'il vaut mieux écrire les tests en début ou en fin de projet ?

Tester pour vérifier

Les tests peuvent être utilisés après le code et servent dans ce cas là simplement à vérifier le code. Ce n'est pas forcément une méthode qui est conseillée car elle est assez ennuyante à écrire et on va avoir tendance à influencer le code du test par rapport à la logique que l'on a déjà écrite. On peut en revanche utiliser cette méthode pour valider le code écrit par quelqu'un d'autre ou lorsque l'on récupère un projet.

Tester puis coder

Aussi appelée Test driven development, cette méthode consiste à écrire les tests avant d'écrire le code. Cela permet de s'assurer que le code que l'on écrit est fonctionnel avant même de l'intégrer à notre application.

Il est d'ailleurs intéressant d'écrire les tests en même temps que l'on écrit le code afin de mieux appréhender un problème.
Par exemple dans le cadre de la création d'un controller on peut s'imaginer faire les choses de cette manière là :

  • On écrit un premier test qui vérifie que la réponse a bien un statut 200.
  • On va ensuite écrire le code qui permet de faire fonctionner ce test (on renvoie une simple instance de Response).
  • On écrit maintenant un second test où l'on attend un titre bien précis
  • On écrit le code permettant de valider ce test
  • On écrit un troisième test pour expliquer que l'on attend un listing de 9 produits
  • On écrit le code permettant de valider ce test
  • Et ainsi de suite...

Cette approche, aussi appelée tests first, permet d'utiliser les tests comme un moyen de décomposer des problèmes complexes en plus petits problèmes simple à résoudre.

Et symfony dans tout ça ?

Symfony dispose par défaut d'une configuration permettant de lancer rapidement les tests à l'aide de PHPUnit.

php bin/phpunit

Il n'y a donc rien à configurer pour commencer à utiliser les tests. En revanche, il peut être difficile de savoir ce qu'il faut tester et comment le tester, c'est pourquoi je vous propose de faire un tour d'horizon des différents composants du framework et de l'écriture des tests associés.