Aller au contenu

Tests : couverture, mutants et tests instables

Le test est le contrôle le plus utilisé en développement logiciel, et le plus utilisé par les orchestrateurs d'agents. La recherche sur son efficacité est ancienne et assez solide. Elle répond à trois questions : comment mesurer qu'une suite de tests est bonne, quelle technique de vérification trouve le plus de défauts, et que faire des tests qui mentent.

La couverture n'est pas une mesure d'efficacité

Le résultat d'Inozemtseva et Holmes (2014)

La couverture de code est la part du code exécutée par les tests (lignes, branches, conditions). C'est la mesure de qualité des tests la plus répandue, et souvent un objectif chiffré (« 80 % de couverture »).

Inozemtseva et Holmes (ICSE 2014, PDF) ont généré 31 000 suites de tests pour cinq systèmes Java allant jusqu'à 724 000 lignes, et mesuré leur efficacité à détecter des défauts injectés. Une fois le nombre de tests contrôlé, la corrélation entre couverture et efficacité est « faible à modérée », et les formes plus fortes de couverture (branches, conditions) n'apportent pas d'information supplémentaire. Conclusion des auteurs : la couverture « ne devrait pas être utilisée comme objectif de qualité ». L'article a reçu le prix de l'article le plus influent d'ICSE en 2024.

La même année, Gopinath, Jensen et Groce (ICSE 2014, PDF) trouvent sur des centaines de projets open source que la couverture d'instructions est le meilleur prédicteur de l'efficacité parmi les mesures de couverture. Les deux résultats se concilient : la couverture est un signal grossier utile pour comparer des projets, mais une mauvaise cible à l'intérieur d'un projet.

Lien avec les agents

Le livre Software Engineering at Google le dit en une phrase : la couverture « mesure seulement qu'une ligne a été exécutée, pas ce qui en a résulté », et un objectif de 80 % tend à devenir un plafond (chapitre 11). Un agent à qui l'on fixe un objectif de couverture écrira des tests qui exécutent des lignes sans rien vérifier — la loi de Goodhart, appliquée par une machine qui optimise.

Les mutants : une meilleure mesure

L'analyse de mutation consiste à injecter de petites modifications artificielles dans le code (remplacer < par <=, supprimer une ligne) et à vérifier que les tests les détectent. Un mutant non détecté signale un test manquant.

Just et al. (FSE 2014, PDF) ont vérifié sur 357 défauts réels que les mutants sont un substitut valable : 73 % des défauts réels sont couplés à un mutant, et la détection des mutants est corrélée à la détection des défauts réels indépendamment de la couverture — plus fortement que la couverture elle-même.

La mutation à l'échelle chez Google

Google a intégré l'analyse de mutation à sa revue de code, en ne présentant que quelques mutants par modification, sur les lignes jugées « intéressantes » (Petrović et Ivanković, ICSE-SEIP 2018 ; Petrović et al., IEEE TSE 2021, arXiv 2102.11378) :

  • plus de 24 000 développeurs, près de 17 millions de mutants générés, 2 millions présentés en revue ;
  • au départ, les développeurs jugeaient 85 % des mutants signalés inutiles ; après filtrage, la part de mutants jugés utiles est passée de 15 % à 89 % ;
  • le nombre médian de mutants par modification est passé de 820 à 7.

Une seconde étude (Petrović et al., ICSE 2021, arXiv 2103.07189, 6 ans, près de 15 millions de mutants) montre que les développeurs exposés à la mutation écrivent plus de tests et des tests plus forts, et que 70 % de 1 502 bugs graves étaient couplés à un mutant qui aurait été signalé sur la modification qui a introduit le bug — modification déjà couverte par des tests. Les auteurs précisent que ce n'est pas une expérience randomisée.

La leçon de Google : le taux de faux positifs d'abord

Le passage de 15 % à 89 % de signalements utiles n'est pas venu d'une meilleure théorie, mais du filtrage de ce qu'on montre au relecteur. C'est la même leçon que l'analyse statique (chapitre Analyse statique) : un contrôle qui produit trop de faux positifs est ignoré, quelle que soit sa justesse théorique.

Quelle technique trouve le plus de défauts ?

Deux expériences fondatrices comparent tests et lecture de code :

  • Myers (1978), Communications of the ACM, 59 praticiens expérimentés, un petit programme : les relectures et inspections sont à peu près aussi efficaces que les tests sur machine ; le meilleur rapport coût-efficacité vient de deux personnes travaillant indépendamment dont on combine les résultats ; la performance individuelle varie énormément.
  • Basili et Selby (1987), IEEE TSE, 32 professionnels et 42 étudiants avancés (résumé) : chez les professionnels, la lecture de code par abstraction trouve plus de défauts que les tests fonctionnels et structurels ; chez les étudiants, pas de différence nette. Les techniques trouvent des types de défauts différents.

Ces deux études, anciennes et sur de petits programmes, posent un principe qui a résisté : les techniques de vérification sont complémentaires, et la diversité des contrôleurs vaut plus que l'intensité d'un seul.

Le problème de l'oracle

Un test a besoin d'un oracle : une manière de savoir si le résultat est correct. La revue de Barr, Harman, McMinn, Shahbaz et Yoo (IEEE TSE 2015, accès libre) classe les oracles en trois familles — spécifiés (spécifications, contrats), dérivés (par exemple les relations métamorphiques), implicites (plantages, exceptions) — et désigne l'automatisation de l'oracle comme le principal goulot de l'automatisation des tests. Quand aucune ne suffit, il reste l'oracle humain.

C'est exactement le problème que posent les tests générés par IA, qui prennent le comportement actuel du code pour l'oracle (Correction et tests générés).

Les tests instables, au-delà de Google

L'étude fondatrice de Luo, Hariri, Eloussi et Marinov (FSE 2014, PDF) classe les causes sur 161 correctifs identifiables :

Cause Part
Attente asynchrone 45 %
Concurrence ~20 %
Dépendance à l'ordre des tests ~12 %

78 % des tests instables le sont dès leur écriture. En Python, Gruber et al. (ICST 2021, arXiv 2101.09077), sur 22 352 projets, trouvent au contraire la dépendance à l'ordre en tête (59 %), et estiment à environ 170 relances le nombre nécessaire pour être sûr à 95 % qu'un test qui passe n'est pas instable. La revue de Parry et al. (TOSEM, 2021) synthétise 76 articles ; 59 % des développeurs interrogés disent rencontrer des tests instables au moins une fois par mois.

Lien avec la règle « trois échecs de suite »

La forte part de causes corrélées (ordre des tests, état partagé) confirme la réserve du chapitre Intégration continue : la règle « k échecs de suite » suppose des échecs indépendants, ce que la dépendance à l'ordre viole.

À retenir

  • La couverture est un signal grossier et une mauvaise cible ; la mutation est une meilleure mesure de l'efficacité d'une suite.
  • À l'échelle industrielle, l'utilité d'un contrôle dépend d'abord de son taux de faux positifs (Google : de 15 % à 89 % de signalements utiles par filtrage).
  • Les techniques de vérification (lecture, tests fonctionnels, structurels) trouvent des défauts différents ; deux contrôleurs indépendants valent mieux qu'un.
  • Les tests instables viennent surtout de l'asynchronisme et de la dépendance à l'ordre, et apparaissent dès l'écriture du test.