Lecture 5 minutes
Pourquoi cet article ?
La plupart d'entre vous savent de quoi il s'agit et l'intérêt des tests unitaires. Cependant, à titre personnel, il m'arrive, perdu dans un bout de code récalcitrant, ou pris par le temps, de me demander si je dois vraiment tester telle ou telle méthode. Il m'arrive de me demander (vraiment très prit par le temps), si je ne devrai pas laisser ce code sans test...
Bref, ici, quelque rappels pour me remettre les idées en place, et qui pourra vous aider également.
Contexte des Tests Unitaires
Avant toute chose, il est bon de rappeler que les tests unitaires ne sont pas des tests fonctionnels.
Exemple :
- « Je souhaite tester l’import d’un fichier, et la présence des produits en BDD après cet import » : TEST FONCTIONNEL
- « Je souhaite vérifier que la méthode « import » de ma classe « Adapter », prenant en paramètre un chemin de fichier, et renvoyant en sortie un tableau fonctionne » : TEST UNITAIRE
- « Je souhaite tester que l’ajout au panier d’un produit hors stock échoue » : TEST FONCTIONNEL
- « Je souhaite tester que la classe « Cart » lève une exception sur appel de « addProduct » avec un produit hors stock » : TEST UNITAIRE
Donc, les tests unitaires sont techniques, et permettent de garantir plusieurs choses :
- Qualité intrinsèque du code : Effectuer ces tests permet de remettre en cause la qualité de notre conception.
- Rétrocompatibilité du code : Lors de l’ajout de nouvelles fonctionnalités sur un module, on peut valider les anciens tests unitaires, et garantir le bon fonctionnement de ce dernier (exemple : Mise à jour de Sync).
- Intégration des modules entre eux : Lors de l’élaboration d’un nouveau module A, intervenant un module B, un lancement des tests unitaires du module B permet de valider que son altération n’a pas détruit le fonctionnel existant.
- Non régression : Valider que le fonctionnel ne régresse pas suite à nos développements.
Quand et comment rédiger un test ?
Cette question est récurrente… L’inquiétude peut venir de nombreux facteurs :
- Est-ce que mon développement nécessite vraiment un test ?
- Où dois-je m’arrêter dans mes tests ?
- Mes tests ne mériteraient t-ils pas d’être testés eux-mêmes ?
- Etc
Il devient alors capital de déterminer ce qui motive un test. Je tente ici de vous faire une liste de cas, mais bien entendu, elle n’est pas exhaustive :
- L’équipe ou le client me remonte un bug : Ce cas justifie pleinement la rédaction d’un test (minimum).
- Je viens de terminer mes développements, il y a trop à faire : Il ne faut pas nécessairement tout couvrir, mais c’est un peu tard : Il fallait les faire au fur et à mesure.
- J’ai fait une classe de mon framework qui doit renvoyer un type spécifique. Dois-je le tester ? : Théoriquement, il faut faire confiance au framework ;-).
- J’ai une méthode qui calcule une promotion, dois-je la tester ? : Je dois vraiment répondre à ça ?
- Oui mais dans quelle mesure ? : Le premier test à réaliser, est le test en situation « normale ». Après, il est possible d’effectuer des tests un petit peu « ésotériques » comme un calcul sur un prix négatif par exemple ? Ou sur un prix qui serait … une chaine de caractères ?
- Ok, donc je teste tous les types ? Tableaux, chaine, float, float+1, etc ? : Il s’agit là d’un très bon exemple. En effet, on peut être tenté de tester tous les cas, mais pour un contexte donné, un seul test est suffisant. Voyons celà en détail.
- Que se passe-t-il si le type est différent > Je créé un test avec un type différent.
- Que se passe-t-il si le numéro dépasse la limite > Je test une limite.
- Attention cependant, dans certains cas, il faudra tester toutes les limites, tout dépend du niveau de sécurité que vous souhaitez apporter à votre développement.
- Notez qu’avec PHPUnit par exemple, il est possible d’utiliser l’annotation « dataProvider » pour effectuer un même test avec différentes valeurs.
Concrètement, cela signifie qu’il faut produire les tests unitaires de manière intelligente sans entrer dans l’excès. J’insiste sur ce dernier point : Nous ne codons pas des applications bancaires, nous n’allons donc pas couvrir la totalité des cas. Cependant, il faut en couvrir suffisamment pour garantir les points décrits plus haut :
- Qualité intrinsèque du code
- Rétrocompatibilité du code
- Intégration des modules entre eux
- Non régression
To Test or not to Test?
"To Test" bien entendu. Mais dans quelle mesure ?
Si on devait résumer la situation : Les tests unitaires, c'est bien, mais à moins de coder une interface bancaire, on ne va pas couvrir notre code à 100%.
Pour conclure, voici les questions affichées près de mon bureau, en cas d'urgence :
- Cette méthode me semble t-elle critique ?
- Ai-je du temps pour écrire plus de tests ?
- J'ai un doute, parlons-en !
- Mieux vaut un petit test que pas de tests du tout.
- J'ai pas le temps, tant pis, je fais au mieux.