20. Les tests : produire des preuves, pas cocher une case
Pourquoi ce chapitre existe
« Écrire des tests » semble être une recommandation incontestable. Elle est pourtant insuffisante : un test peut passer tout en protégeant le mauvais comportement, être si couplé à l'implémentation qu'il empêche le refactoring, ou être si lent qu'il n'aide plus personne à décider. À l'inverse, l'absence d'unité testable ne signifie pas automatiquement l'absence de preuve ; une règle peut être protégée par un contrat, une simulation, un rapprochement ou une observation de production.
La question d'ingénierie n'est donc pas combien de tests écrire. C'est : quelle affirmation importante ce changement doit-il rendre crédible, et quelle preuve est proportionnée à son risque ?
Les idées essentielles
- Un test est une expérience qui relie des conditions à une attente observable.
- Une preuve est forte lorsqu'elle est proche du risque étudié, déterministe, lisible et difficile à satisfaire par accident.
- Aucun niveau de test ne prouve tout ; une stratégie équilibre précision, réalisme, vitesse et coût de diagnostic.
- Les tests servent à la fois à prévenir une régression, à expliquer le comportement et à accélérer l'apprentissage.
Partir d'une affirmation falsifiable
« La remise fonctionne » n'est pas testable. « Une commande ne reçoit jamais une remise supérieure à son montant éligible, quelle que soit la combinaison de coupons applicables » l'est. Cette formulation conduit à des cas représentatifs, des bornes et des invariants. Elle évite d'écrire un test qui reproduit l'implémentation sans interroger le résultat.
Pour chaque changement important, l'équipe peut associer trois éléments : le comportement à préserver ou introduire, le dommage s'il est faux, et la preuve qui rendrait cet écart visible. Cette discipline révèle les angles morts : un test local peut valider le calcul, mais pas le schéma publié ; un test de parcours peut valider l'intégration, mais pas la performance sous charge.
| Risque | Preuve souvent pertinente | Angle mort à reconnaître |
|---|---|---|
| Règle métier | Cas de décision, propriétés ou invariants | Intégration avec les données et transports réels |
| Régression d'API | Test de contrat producteur-consommateur | Parcours métier de bout en bout |
| Migration | Contraintes, comptes attendus, réconciliation | Comportements d'anciens clients non simulés |
| Incident opérationnel | Test de résilience, alerte, exercice de runbook | Variabilité complète de production |
Les tests doivent échouer pour une bonne raison
Un test utile indique quel comportement a changé et pourquoi cela compte. S'il échoue au gré de l'heure, d'un ordre d'exécution, d'une donnée partagée ou d'un réseau externe, il érode la confiance dans toute la suite. Les tests instables ne sont pas une nuisance mineure : ils enseignent aux équipes à ignorer les signaux, précisément l'inverse de ce que doit produire une stratégie de qualité.
Un test peut légitimement vérifier une interaction lorsqu'elle fait partie du contrat — un paiement n'est débité qu'une fois, un événement est publié. Il devient fragile lorsqu'il vérifie chaque appel interne uniquement parce que c'est facile à observer. Formuler l'attente dans le vocabulaire du domaine protège mieux le comportement et autorise les améliorations de structure.
Anti-patterns
Traiter la couverture comme objectif de qualité
La couverture mesure les lignes ou branches exécutées, non la pertinence des attentes. Elle peut révéler une zone sans aucun test ; elle ne doit pas devenir un seuil qui incite à exécuter du code sans l'interroger.
Ajouter un test après l'incident sans comprendre le mécanisme
Un incident peut justifier un test de non-régression. S'il provient aussi d'un contrat ambigu, d'une donnée historique ou d'une alerte absente, le test seul laisse intacte la famille de risques. L'action doit couvrir la cause systémique identifiée.
Simuler tout le monde
Des mocks très détaillés donnent un contrôle total mais reproduisent souvent les hypothèses de l'auteur. Ils sont utiles pour isoler une décision ; ils ne remplacent pas une preuve contre le contrat réellement fourni par une dépendance.
Bonnes pratiques
Tenir une carte de preuves légère
Pour un parcours sensible, documenter les propriétés importantes et les mécanismes qui les couvrent. Cette carte peut vivre dans la PR, la spécification ou la documentation du service. Elle évite les doublons de tests et rend visibles les risques volontairement non couverts.
Investir dans la vitesse de diagnostic
Un échec rapide, déterministe et précis raccourcit le cycle de développement. Les équipes devraient suivre et traiter la lenteur, l'instabilité et l'opacité de la suite comme des problèmes de produit interne, car elles modifient directement la capacité à prendre de bonnes décisions.
Checklist — Une preuve est-elle utile ?
- Quelle affirmation de comportement cette preuve soutient-elle ?
- Quel risque ou quel dommage rend cette affirmation importante ?
- Le test échouerait-il si le comportement changeait réellement de manière dangereuse ?
- Est-il déterministe, lisible et suffisamment rapide pour être utilisé au bon moment ?
- Quel angle mort reste-t-il après son passage ?
- Qui saura interpréter l'échec et agir dessus ?
À retenir
Les tests ne sont pas une collection de fichiers verts. Ce sont des preuves ciblées qui rendent un comportement discutable, protègent les invariants et raccourcissent le délai entre une erreur et sa compréhension.