On en a parlé dans le chapitre sur les
environnements, les tests automatiques sont
au coeur du développement logiciel moderne. Mais concrètement, de quoi parle-t-on ?
Un test automatique, c'est du code qui vérifie que d'autre code fonctionne correctement.
Au lieu de cliquer manuellement sur chaque bouton du site pour vérifier que tout marche,
les développeurs écrivent des programmes qui font ces vérifications à leur place.
Une fois écrits, ces tests peuvent être relancés des dizaines de fois par jour sans effort, et le sont
automatiquement à chaque modification du code par la
chaîne d'intégration continue.
Ils sont la première ligne de défense contre les bugs et les régressions.
Les différents niveaux de tests
On distingue classiquement trois niveaux, du plus petit au plus large, chacun vérifiant une chose différente.
Les tests unitaires
Ils testent une seule petite brique du code, de manière isolée.
Prenons une fonction qui calcule un prix TTC. Mon test unitaire vérifie que
calculerPrixTTC(100, 20) retourne bien 120, et que
calculerPrixTTC(0, 20) retourne 0. C'est rapide, ciblé, et si ça casse,
on sait immédiatement où est le problème.
Les tests unitaires sont les plus nombreux dans un projet. Un bon projet peut en avoir
des centaines, voire des milliers. Ils s'exécutent en quelques secondes.
Les tests fonctionnels (ou d'intégration)
Ils ne testent plus une brique isolée mais plusieurs qui travaillent ensemble.
Cette fois, je simule un utilisateur qui se connecte, ajoute un produit au panier, entre ses
coordonnées bancaires et valide le paiement. Le test vérifie que la commande est bien créée,
que le stock est mis à jour et que l'email de confirmation est envoyé.
Ces tests sont plus lents car ils sollicitent une bonne partie du système, mais ils vérifient que
les pièces du puzzle s'assemblent correctement.
Les tests de bout en bout
Dernier niveau, le plus proche du réel. Un robot pilote un vrai navigateur sur une vraie version du site,
clique sur les boutons et remplit les formulaires comme le ferait un client.
On dit aussi "end-to-end", souvent abrégé E2E. Ce sont les tests les plus convaincants, puisqu'ils vérifient
exactement ce que l'utilisateur vivra. Ce sont aussi les plus lents, les plus coûteux à maintenir, et ceux qui
échouent le plus souvent pour de mauvaises raisons, un bouton déplacé ou une page un peu longue à charger.
Le BDD : décrire le comportement attendu
Le Behavior Driven Development (BDD) est une approche qui rapproche les développeurs
des équipes métier. Les critères de validation d'une fonctionnalité y sont écrits
dans un langage compréhensible par tout le monde, pas seulement par les développeurs.
Ce langage s'appelle Gherkin, et l'outil qui l'a rendu populaire s'appelle Cucumber. La structure est
volontairement simple :
Étant donné un client avec un abonnement premium
Quand il ajoute un produit à 50€ au panier
Alors une réduction de 10% est appliquée
Et le total affiché est de 45€
Ce scénario est écrit en collaboration avec le demandeur (chef de produit, product owner...).
Le développeur le transforme ensuite en test automatique. Si le test passe, la fonctionnalité
est validée.
L'intérêt du BDD, c'est que les critères de validation sont clairs pour tout le monde
avant le début du développement. Ça évite les malentendus et les allers-retours.
Le TDD : tester d'abord, coder ensuite
Le Test Driven Development (TDD) pousse la logique encore plus loin, puisqu'on écrit
le test avant le code.
Le cycle est simple :
- Rouge : écrire un test qui échoue (le code n'existe pas encore).
- Vert : écrire le minimum de code pour faire passer le test.
- Refactoring : améliorer le code tout en s'assurant que le test passe toujours.
Ce cycle se répète en boucle, test par test, fonctionnalité par fonctionnalité.
Le TDD est un sujet qui divise la communauté. Ses partisans affirment qu'il produit
un code mieux conçu, plus testable et plus fiable. Ses détracteurs trouvent que c'est
trop rigide et que ça ralentit le développement.
De mon expérience, la vérité est entre les deux. Le TDD est un excellent exercice
pour les développeurs en formation. Il force à réfléchir au "quoi" avant le "comment".
En pratique, les développeurs expérimentés alternent entre TDD et approche classique
selon la complexité du sujet.
La pyramide des tests
Ces trois niveaux ne se valent pas en quantité. L'image qui circule depuis une quinzaine d'années, popularisée par Mike Cohn, est celle d'une pyramide :
- Base (large) : beaucoup de tests unitaires, rapides et ciblés.
- Milieu : moins de tests d'intégration, qui vérifient les assemblages.
- Sommet (étroit) : très peu de tests de bout en bout, qui rejouent les quelques parcours vraiment critiques, typiquement l'inscription et le paiement.
L'erreur classique consiste à inverser la pyramide, en n'ayant que des tests de bout en bout.
Ils sont lents, fragiles et quand ils échouent, on ne sait pas d'où vient le problème.
Un projet sans tests, c'est un avion sans instruments de bord. Ça vole, mais personne
n'ose toucher à quoi que ce soit de peur que tout s'effondre. Les mises en production
deviennent stressantes, et les bugs sont découverts par les utilisateurs au lieu des développeurs.