5. Construire une protection précise et maintenable
5.1 Plusieurs niveaux de tests, avec des coûts différents
La conférence se concentre sur les tests unitaires, les petites vérifications des composants. Ils doivent être nombreux à la base de la pyramide. Les tests plus larges, plus coûteux et plus difficiles à entretenir doivent être moins nombreux et choisis selon leur objectif.
À une question explicite, l’intervenant répond que de bons tests unitaires n’éliminent pas les autres tests. Une composante isolée peut fonctionner sans que toutes les couches soient correctement reliées. Les tests d’intégration, d’acceptation et de bout en bout répondent donc à d’autres questions.
Son exemple de bout en bout suit une action Web à travers plusieurs couches, jusqu’à un traitement et au retour d’une valeur numérique. Si le but de ce scénario est de vérifier le branchement des couches, constater qu’une valeur revient peut suffire; les détails de calcul peuvent déjà être couverts ailleurs. Cela ne signifie pas que tous les tests de bout en bout doivent ignorer leur résultat. Il faut définir ce que chacun cherche à garantir.
Un test supérieur qui répète tous les détails de l’algorithme devient fragile : une modification interne correcte exige alors de changer beaucoup de tests coûteux. Un mauvais découpage des vérifications peut ralentir le changement et donner l’impression que les tests sont le problème, alors que leur portée a été mal choisie.
5.2 Découper le code pour isoler les comportements
Quand tout est lié, une erreur provoque des échecs en cascade et le diagnostic devient imprécis. Le cas extrême montré oralement est un énorme main où les conditions et responsabilités s’enchaînent sans séparation réelle en fonctions ou classes.
Une difficulté persistante à tester une partie peut donc être un signal de mauvais découpage, d’architecture ou de modélisation. Le conférencier recommande SOLID et l’usage approprié des patrons de conception, sans abus. Il indique que les cinq principes SOLID font partie de ses questions d’entrevue, mais ils ne sont pas enseignés en détail dans cette conférence.
Il faut distinguer ce mauvais couplage du mode de déploiement. On peut livrer une application en un bloc tout en ayant des composants clairement séparés à l’intérieur. La testabilité n’impose donc pas de tout transformer en microservices.
5.3 Un aspect par test, un diagnostic lisible
Un test unitaire doit cibler un seul aspect d’une méthode. Son nom devrait permettre de comprendre le comportement qui échoue. Vérifier de nombreuses choses hétérogènes dans un même scénario rend plus difficile l’interprétation du résultat et la localisation de la cause.
La description est aussi importante dans un test paramétré. Un seul corps peut générer plusieurs exécutions; quand les combinaisons d’arguments deviennent complexes, une description additionnelle aide à identifier celle qui échoue, sans recopier toute la logique du test.
Le choix de l’assertion participe à cette précision. La démonstration occasionne un échange sur l’égalité et l’équivalence : pour des objets ou collections, les règles de comparaison et l’ordre peuvent changer la signification du contrôle. Comprendre ce que vérifie l’outil compte davantage que choisir une méthode d’assertion parce que l’autocomplétion la propose.
5.4 Indépendance, concurrence et rétroaction rapide
Les tests doivent éviter les dépendances d’ordre et les effets de bord entre eux. Une suite qui fonctionne seulement dans une séquence donnée est fragile et plus difficile à distribuer. Chaque scénario doit disposer de la préparation nécessaire à sa propre exécution.
L’indépendance permet aussi la concurrence et la répartition sur plusieurs machines. Le conférencier évoque le cloud pour maintenir un délai de rétroaction raisonnable quand la suite grossit : davantage de ressources peut coûter moins cher que découvrir chaque erreur très tard.
Il faut en outre ordonner les niveaux de validation. On commence par les tests unitaires, puis on lance les contrôles plus coûteux si le premier niveau passe. Exécuter toute une suite d’acceptation ou de bout en bout sur une version déjà rejetée par un contrôle élémentaire gaspille des ressources. L’objectif reste de réduire le temps entre le changement, son intégration et un résultat de validation utile.
5.5 Interfaces et objets simulés
Une interface permet de définir l’interaction avec une dépendance sans imposer son implémentation concrète au test. Un mock, ou objet simulé, peut remplacer cette dépendance avec un comportement contrôlé. L’exemple oral configure une réponse du type « recevoir 28, retourner 45 » pour permettre de vérifier la composante cible indépendamment du comportement réel de l’autre objet.
Cette isolation évite qu’un bogue de la dépendance fasse échouer toute une chaîne de tests sans indiquer la composante responsable. Elle ne vérifie toutefois pas à elle seule la bonne intégration des objets réels; cette autre question reste nécessaire.
Les frameworks de mocks peuvent aider, mais certains ajoutent de la complexité et du couplage qui réduisent l’agilité. Trop simuler peut aussi conduire à ne plus tester de comportement significatif. Il faut conserver un test lisible, une cible claire et des vérifications complémentaires des interactions réelles.