Tests et documentation générés
Comment exploiter la génération automatique de tests et de documentation sans confondre couverture apparente et vérification réelle du comportement.
Table des matières
Pourquoi la génération automatisée change la donne
Écrire des tests coûte du temps, et ce temps est le premier sacrifié quand une échéance approche. Un assistant capable de produire un jeu de tests exploitable en quelques secondes change donc l'équation économique du projet. Mais cette économie n'est réelle que si les tests produits vérifient effectivement un comportement — pas seulement s'ils s'exécutent sans erreur et remontent un pourcentage de couverture flatteur.
Le même raisonnement s'applique à la documentation. Un modèle de langagegrand modèle de langageIAModèle entraîné à prédire le token suivant d'une séquence de texte. Toutes ses capacités apparentes — résumer, traduire, coder — découlent de cette unique tâche.Voir dans le glossaire rédige un README convaincant à partir d'un simple survol du code. Convaincant ne signifie pas exact. Ce chapitre pose la question centrale de tout usage sérieux de ces outilstool callingIACapacité d'un agent ou d'un LLM à invoquer des outils externes (API, calcul, recherche) pendant le raisonnement.Voir dans le glossaire : comment vérifier qu'un artefact généré — test ou documentation — correspond au comportement réel du système, et non à ce qu'il est simplement censé faire ?
Ce que la génération de tests fait réellement bien
Utilisée correctement, la génération automatisée de tests apporte un gain net sur plusieurs points précis :
- Squelette de suite de tests à partir d'une fonction ou d'une classe : structure, nommage, montage des fixtures — le travail mécanique qui décourage souvent d'en écrire davantage.
- Cas limites systématiques : chaînes vides, valeurs nulles, bornes numériques, caractères Unicode, listes vides — la couverture des cas qu'un développeur pressé oublie presque toujours.
- Tests de caractérisation lors d'un refactor : figer le comportement observé d'un code existant avant de le modifier, pour détecter toute régression involontaire.
- Traduction de spécifications informelles en assertions exécutables, à partir d'un ticket ou d'une description en langage naturel.
Ces usages ont un point commun : dans chacun, un humain reste responsable de définir ce que le comportement devrait être. L'IAintelligence artificielleIAEnsemble des techniques permettant à un programme d'accomplir une tâche qui demanderait de l'intelligence humaine. Le terme couvre aussi bien les systèmes à règles écrites que ceux qui apprennent de données.Voir dans le glossaire accélère l'écriture, elle ne décide pas de la vérité métier.
Un test de caractérisation documente ce que le code fait aujourd'hui, bugs compris — utile pour sécuriser un refactor. Un test de spécification documente ce que le code doit faire. Généré sans distinction, un test d'IA peut figer un bug en le faisant passer pour un comportement voulu. Demandez toujours à l'outil, et à vous-même, lequel des deux vous êtes en train d'écrire.
Le piège numéro un : la couverture illusoire
Un taux de couverture de lignes élevé ne dit rien sur la qualité des assertions. Un test peut exécuter cent pour cent des branches d'une fonction sans vérifier aucun résultat significatif.
Exemple représentatif, généré tel quel par un assistant sur une fonction de calcul de remise :
def test_calculer_remise():
resultat = calculer_remise(100, 0.2)
assert resultat is not None
Ce test passe, augmente la couverture affichée, et ne détecterait pas une remise calculée à 15 % au lieu de 20 %. Le générateur a produit un appel de fonction, pas une vérification. C'est le symptôme le plus fréquent des suites de tests générées sans relecture : des assertions faibles (is not None, assert True, absence de valeur attendue explicite) qui gonflent la couverture sans réduire le risque.
Deuxième variante, plus insidieuse : le test tautologique, où l'assertion recopie la logique même du code testé plutôt qu'une valeur attendue indépendante.
def test_conversion():
resultat = convertir_devise(100, taux=1.08)
assert resultat == 100 * 1.08 # recopie le calcul, ne le vérifie pas
Si la fonction contient une erreur dans son taux de conversion, le test la reproduira fidèlement et passera quand même. Un test valable compare le résultat à une valeur attendue calculée indépendamment — à la main, ou tirée d'un cas documenté par ailleurs.
Autre défaillance classique en génération automatique : mocker la dépendance qu'on est précisément censé tester. Sur du code métier interfacé avec une base de donnéesdonnéesIAEnsemble d'informations structurées ou non utilisées pour entraîner, évaluer ou alimenter un modèle. La qualité, la quantité et la représentativité des données sont les facteurs décisifs pour les performances en apprentissage automatique.Voir dans le glossaire, un assistant produit parfois un test qui mocke la fonction de calcul elle-même pour « isoler l'appel », laissant le mock renvoyer une valeur arbitraire jamais confrontée au vrai calcul. Le test devient vert quel que soit le contenu réel de la fonction. Posez-vous systématiquement la question : qu'est-ce que ce test laisserait passer si le code sous test était cassé ?
Mesurer la couverture réelle, au-delà du pourcentage de lignes
Le pourcentage de lignes couvertes (coverage.py, istanbul/nyc, JaCoCo) reste utile pour repérer le code jamais exécuté par la suite de tests — mais il ne mesure pas la force des assertions. Deux compléments changent la donne.
Le mutation testing (mutmut en Python, Stryker en JavaScript/TypeScript, PIT en Java) modifie automatiquement le code source — inverser une condition, changer un opérateur, décaler une borne — puis vérifie si la suite de tests détecte la modification. Un mutant « tué » signifie qu'au moins un test a échoué suite au changement ; un mutant « survivant » signifie qu'aucun test ne s'en est aperçu, ce qui révèle une assertion trop faible même avec une couverture de lignes à cent pour cent.
Une suite de tests peut afficher 95 % de couverture de lignes et 20 % de mutants tués. La première mesure indique ce qui a été exécuté ; la seconde indique ce qui a été réellement vérifié. Pour une suite générée automatiquement, plus exposée aux assertions faibles, le taux de mutants tués est l'indicateur qui compte vraiment.
La revue ciblée sur les chemins critiques. Viser cent pour cent de couverture globale a un coût élevé pour un bénéfice marginal sur du code peu risqué (accesseurs, configuration statique). Concentrer l'effort — humain et de mutation testing — sur les fonctions qui touchent à l'argent, aux droits d'accès, à l'intégrité des données ou à la conformité réglementaire est presque toujours plus rentable.
Documentation générée : docstrings, README, ADR
La génération automatique de documentation expose un risque symétrique à celui des tests : la forme est soignée, le fond peut être faux.
- Docstrings générées à partir d'une signature de fonction : l'outil décrit souvent les types de paramètres avec précisionprécisionIAProportion des alertes émises par un modèle qui sont justifiées. Elle s'oppose au rappel : améliorer l'une dégrade l'autre.Voir dans le glossaire, mais devine le pourquoi — l'intention métier — quand elle n'est pas explicite dans le code ou les commentaires existants. Une docstring qui paraphrase le nom de la fonction n'apporte rien ; une docstring qui invente une contrainte non implémentée induit en erreur.
- README généré à partir d'un survol du dépôt : utile pour un premier jet de structure (installation, arborescence, commandes), mais l'outil peut halluciner une fonctionnalité déduite du nom d'un module qui n'a jamais été implémentée, ou omettre une variable d'environnement obligatoire absente du code qu'il a parcouru.
- ADR (Architecture Decision Record) : l'IA aide à formaliser une décision déjà prise — mettre en forme le contextefenêtre de contexteIAQuantité de texte qu'un modèle peut prendre en compte simultanément : question, documents fournis et historique. Au-delà, les éléments les plus anciens sortent du champ.Voir dans le glossaire, les alternatives, les conséquences — mais ne doit jamais se substituer à la décision elle-même, qui engage une responsabilité d'équipe.
Fonction source :
def retry(fn, max_attempts=3): ..., qui ne gère en réalité aucun délai entre tentatives. Docstring générée : « Réessaie l'appel avec un backoff exponentiel jusqu'à max_attempts fois. » Le terme « backoff exponentiel » ne correspond à aucun mécanisme du code : l'outil l'a inféré du nom de la fonction et du vocabulaire habituel associé àretry. Une revue rapide du corps de la fonction suffit à repérer l'écart, mais seulement si quelqu'un la fait.
La dérive documentation-code
Une documentation générée une fois puis jamais régénérée devient, avec le temps, pire que l'absence de documentation : elle inspire une confiance que le code ne justifie plus. Le risque n'est pas propre à la génération par IA — il existe depuis toujours — mais la facilité de production augmente le volume de documentation en circulation, donc le volume susceptible de dériver.
Deux pratiques limitent ce risque :
- Documentation comme code : les docstrings et schémas générés vivent dans le dépôt, versionnés avec le code, revus en pull request au même titre qu'une modification fonctionnelle.
- Régénération liée à la CI : un job qui régénère, ou signale l'obsolescence de, la documentation API à chaque changement de signature publique, plutôt qu'une génération ponctuelle oubliée après le premier sprint.
Le fait qu'un texte ait été produit par un modèle ne change rien à son cycle de vie une fois committé. La responsabilité de le maintenir à jour revient entièrement à l'équipe, exactement comme pour un texte écrit à la main.
Méthode de travail recommandée
Une suite de tests ou une documentation générée gagne à suivre un même protocole de vérification avant d'entrer en production :
| Étape | Objectif | Qui |
|---|---|---|
| 1. Génération | Produire un premier jet à partir du code ou de la spécification | Assistant IA |
| 2. Lecture des assertions | Repérer les assertions faibles, tautologiques ou absentes | Développeur |
| 3. Test de la casse | Modifier volontairement le code source pour vérifier que le test échoue | Développeur |
| 4. Mutation testing | Confirmer que les mutants sur le code critique sont majoritairement tués | CI |
| 5. Revue ciblée | Concentrer l'attentionattentionIAMécanisme par lequel un modèle pondère l'importance de chaque token du contexte lorsqu'il en traite un autre, quelle que soit la distance qui les sépare.Voir dans le glossaire humaine sur les chemins à risque métier ou sécurité | Reviewer |
| 6. Fusion | Intégrer une fois les cinq étapes précédentes validées | Équipe |
L'étape 3, casser volontairement le code pour vérifier que le test échoue, est la plus souvent sautée et la plus rentable : elle prend quelques secondes et révèle immédiatement un test qui ne teste rien.
Ce qu'il ne faut jamais déléguer entièrement
Certains éléments restent hors du périmètre raisonnable de délégation, quelle que soit la qualité de l'outil :
- Le comportement attendu sur les flux sensibles — paiement, authentification, droits d'accès, conformité réglementaire. La spécification doit venir d'un humain qui porte la responsabilité métier.
- L'arbitrage entre couverture et coût — décider où investir l'effort de test est une décision de gestion de risque, pas une tâche mécanique.
- La décision d'architecture documentée dans un ADR — l'IA formalise, elle ne tranche pas.
- La correction d'une divergence détectée entre documentation et code — corriger l'un des deux suppose de savoir lequel est faux, ce qui exige une compréhension du contexte métier que l'outil n'a pas.
Checklist avant de faire confiance à une suite générée
Avant de fusionner une suite de tests ou une documentation générée, vérifier :
- Chaque test échoue-t-il si on casse volontairement la fonction testée ?
- Les assertions comparent-elles à une valeur attendue indépendante, ou recopient-elles le calcul du code testé ?
- Le sujet testé est-il mocké par erreur au lieu de sa dépendance ?
- Le taux de mutants tués sur le code critique est-il satisfaisant, pas seulement le taux de lignes couvertes ?
- La documentation générée a-t-elle été confrontée au corps réel de la fonction, pas seulement à sa signature ?
- Existe-t-il un mécanisme de régénération ou d'alerte d'obsolescence pour la documentation publiée ?
Cette liste ne remplace pas la revue de code habituelle ; elle ajoute les points de vigilance spécifiques aux artefacts générés automatiquement, là où l'assurance apparente du texte produit peut faire baisser la garde du relecteur.
Ce qu'il faut retenir
La génération automatisée de tests et de documentation réduit un coût réel — celui du temps d'écriture — sans réduire le coût de la vérification, qui reste entièrement humain. Un test généré sans lecture des assertions, sans mutation testing et sans tentative de casse volontaire n'apporte qu'une couverture de façade. Une documentation générée sans confrontation au code réel, et jamais régénérée, dérivedériveIADégradation progressive des performances d'un modèle après son déploiement, causée par l'évolution des comportements ou du contexte. Elle impose surveillance et réentraînement.Voir dans le glossaire silencieusement jusqu'à devenir trompeuse. Traiter ces deux artefacts comme du code à part entière — versionnés, revus, vérifiés par des méthodes qui mesurent la force réelle de la vérification et non son apparence — permet de capter le gain de vitesse sans hériter d'une fausse sécurité.
L'essentiel à retenir
Ce chapitre distingue la couverture de tests apparente de la couverture réelle : un test généré peut passer sans vérifier aucun comportement significatif, via des assertions faibles, des tests tautologiques ou le mock du sujet testé lui-même. Il présente le mutation testing comme mesure complémentaire à la couverture de lignes, et une méthode en six étapes pour vérifier une suite générée avant fusion. Il traite ensuite la documentation générée — docstrings, README, ADR — et le risque symétrique de dérive entre documentation et code réel dans le temps. Une checklist opérationnelle et un tableau de méthode clôturent le chapitre.
Questions fréquentes
Un test généré par IA qui passe est-il fiable ?
Comment savoir si mes tests testent vraiment quelque chose ?
Faut-il viser 100 % de couverture de code ?
Peut-on faire confiance à une documentation générée automatiquement à partir du code ?
Quelle est la différence entre un test de caractérisation et un test unitaire classique ?
Comment éviter que la documentation générée devienne obsolète ?
Un test qui mocke une dépendance est-il toujours suspect ?
Progression sauvegardée dans votre navigateur.
Quiz de validation
Quiz Player
Quiz de validation
Plusieurs réponses possibles — validez ensuite.
Vrai ou faux.
Quiz indisponible (données invalides).