La couverture de code figure parmi les premières méthodes inventées pour les tests logiciels systématiques . La première référence publiée est celle de Miller et Maloney dans Communications of the ACM , en 1963.
suite de tests , un ou plusieurs critères de couverture sont utilisés. Ceux-ci sont généralement définis comme des règles ou des exigences qu'une suite de tests doit respecter.Critères de couverture de base
Il existe un certain nombre de critères de couverture, mais les principaux sont :
- Couverture fonctionnelle – chaque fonction (ou sous-programme ) du programme a-t-elle été appelée ?
- Couverture des instructions – chaque instruction du programme a-t-elle été exécutée ?
- Couverture des arêtes – chaque arête du graphe de flux de contrôle a-t-elle été exécutée ?
- Couverture des branches : chaque branche (également appelée chemin DD ) de chaque structure de contrôle (comme dans les instructions if et case ) a-t-elle été exécutée ? Par exemple, pour une instruction if , les branches « vrai » et « faux » ont-elles toutes deux été exécutées ? (Il s’agit d’un sous-ensemble de la couverture des arêtes . )
- Couverture des conditions – chaque sous-expression booléenne a-t-elle été évaluée à la fois à vrai et à faux ? (Également appelée couverture des prédicats.)C suivante :0) && (y > 0)) { z = x; } return z; } "
int foo ( int x , int y ) { int z = 0 ; if (( x > 0 ) && ( y > 0 )) { z = x ; } return z ; }
Supposons que cette fonction fasse partie d'un programme plus vaste et que ce programme ait été exécuté avec une suite de tests.
- La couverture fonctionnelle sera assurée si, au cours de cette exécution, la fonction
fooa été appelée au moins une fois. - La couverture des instructions pour cette fonction sera satisfaite si elle était appelée par exemple comme
foo(1,1), car dans ce cas, chaque ligne de la fonction serait exécutée, y comprisz = x;. - La couverture des branches sera satisfaite par les tests appelant
foo(1,1)etfoo(0,1)car, dans le premier cas, les deuxifconditions sont remplies etz = x;est exécuté, tandis que dans le second cas, la première condition,(x>0), n'est pas satisfaite, ce qui empêche l'exécution dez = x;. - La couverture des conditions sera assurée par des tests appelant `if`
foo(1,0),foo(0,1)`if` et `foo(1,1)if`. Ces tests sont nécessaires car, dans le premier cas, `if`(x>0)est évalué à `true`true, tandis que dans le second, il est évalué à `falsefalse`. Par ailleurs, le premier cas renvoie `true`(y>0)false, le deuxième cas ne renvoie pas `true`(y>0)(en raison de l'évaluation paresseuse de l'opérateur booléen), et le troisième cas renvoie `false`true.
Dans les langages de programmation qui n'effectuent pas d'évaluation court-circuitée , la couverture des conditions n'implique pas nécessairement la couverture des branches. Par exemple, considérons le fragment de code Pascal suivant :
L'injection de fautes peut être nécessaire pour garantir que toutes les conditions et branches du code de gestion des exceptions soient correctement couvertes lors des tests.point d'entrée et de sortie du programme ait été appelé au moins une fois, et que chaque décision du programme ait été testée sur tous ses résultats possibles au moins une fois. Dans ce contexte, la décision est une expression booléenne comprenant des conditions et zéro ou plusieurs opérateurs booléens. Cette définition diffère de la couverture de branche , mais le terme « couverture de décision » est parfois utilisé comme synonymeCouverture des conditions/décisions modifiées
La couverture condition/décision exige que les couvertures de décision et de condition soient toutes deux satisfaites. Cependant, pour les applications critiques pour la sécurité (telles que les logiciels avioniques ), il est souvent nécessaire de satisfaire une couverture condition/décision modifiée (MC/DC) . Ce critère étend les critères condition/décision en exigeant que chaque condition affecte le résultat de la décision de manière indépendante.
Par exemple, considérons le code suivant :
un b c vrai vrai vrai FAUX FAUX FAUX Cependant, l'ensemble de tests ci-dessus ne satisfait pas aux exigences de couverture de condition/décision modifiée, car dans le premier test, la valeur de « b » et dans le second, la valeur de « c » n'influencent pas le résultat. Par conséquent, l'ensemble de tests suivant est nécessaire pour satisfaire ces exigences :
un b c FAUX vrai FAUX FAUX vrai vrai FAUX FAUX vrai vrai FAUX vrai Couverture multi-affections
Ce critère exige que toutes les combinaisons de conditions au sein de chaque décision soient testées. Par exemple, l'extrait de code de la section précédente nécessitera huit tests :
un b c FAUX FAUX FAUX FAUX FAUX vrai FAUX vrai FAUX FAUX vrai vrai vrai FAUX FAUX vrai FAUX vrai vrai vrai FAUX vrai vrai vrai Couverture des valeurs des paramètres
La couverture des valeurs des paramètres (PVC) exige que, dans une méthode prenant des paramètres, toutes les valeurs courantes de ces paramètres soient prises en compte. L'idée est de tester toutes les valeurs possibles courantes d'un paramètre. Par exemple, les valeurs courantes d'une chaîne de caractères sont : 1) null , 2) vide, 3) espaces (espaces, tabulations, sauts de ligne), 4) chaîne valide, 5) chaîne invalide, 6) chaîne sur un octet, 7) chaîne sur deux octets. Il peut également être approprié d'utiliser des chaînes très longues. Ne pas tester toutes les valeurs possibles d'un paramètre peut entraîner un bogue. Tester une seule de ces valeurs permettrait d'obtenir une couverture de code de 100 % puisque chaque ligne est couverte, mais comme seule une des sept options est testée, la PVC n'est que de 14,2 %.
Autres critères de couverture
Il existe d'autres critères de couverture, qui sont moins fréquemment utilisés :
- Couverture des séquences de code linéaires et des sauts (LCSAJ) , également appelée couverture des chemins JJ : chaque LCSAJ/JJ-path a-t-il été exécuté ?machine à états finis a-t- il été atteint et exploré ?
- Couverture du flux de données – Chaque définition de variable et son utilisation ont-elles été atteintes et explorées ? Les applications critiques pour la sécurité ou nécessitant une grande fiabilité doivent souvent démontrer une couverture de test de 100 %. Par exemple, la norme ECSS -E-ST-40C exige une couverture de 100 % des instructions et des décisions pour deux des quatre niveaux de criticité ; pour les autres, les valeurs cibles de couverture sont négociées entre le fournisseur et le client. Cependant, la fixation de valeurs cibles spécifiques, et en particulier de 100 %, a été critiquée par les praticiens pour diverses raisons (cf. ). Martin Fowler écrit : « Je me méfierais de toute valeur de 100 % ; cela donnerait l’impression que quelqu’un conçoit des tests uniquement pour obtenir de bons chiffres de couverture, sans réfléchir à la démarche. »
Certains critères de couverture mentionnés ci-dessus sont liés. Par exemple, la couverture des chemins implique la couverture des décisions, des instructions et des entrées/sorties. La couverture des décisions implique la couverture des instructions, car chaque instruction fait partie d'une branche.
La couverture complète des chemins, telle que décrite ci-dessus, est généralement impraticable, voire impossible. Tout module contenant une succession de décisions peut comporter jusqu'à n chemins ; les boucles peuvent générer un nombre infini de chemins. De nombreux chemins peuvent également être irréalisables, c'est-à-dire qu'aucune entrée du programme testé ne permet de les exécuter. Cependant, il a été démontré qu'il est impossible de concevoir un algorithme général pour identifier les chemins irréalisables (un tel algorithme pourrait être utilisé pour résoudre le problème de l'arrêt ). Le test des chemins de base est par exemple une méthode permettant d'obtenir une couverture complète des branches sans pour autant obtenir une couverture complète des chemins.
Les méthodes de test de couverture de chemin pratiques tentent plutôt d'identifier des classes de chemins de code qui ne diffèrent que par le nombre d'exécutions de boucle, et pour atteindre une couverture de « chemin de base », le testeur doit couvrir toutes les classes de chemins. couverture des instructions, des branches/décisions, des conditions/décisions modifiées (MC/DC) et des séquences de code linéaires et des sauts (LCSAJ ).
- La couverture sera-t-elle mesurée par rapport à des tests qui vérifient les exigences imposées au système testé ( DO-178B ) ?
- Le code objet généré est-il directement traçable aux instructions du code source ? Certaines certifications (par exemple, DO-178B niveau A) exigent une couverture au niveau de l’assembleur si ce n’est pas le cas : « Dans ce cas, une vérification supplémentaire doit être effectuée sur le code objet afin d’établir la correction de ces séquences de code générées » ( DO-178B ) paragraphe 6.4.4.2.
Les développeurs de logiciels peuvent analyser les résultats de couverture de test pour concevoir des tests supplémentaires et des ensembles d'entrées ou de configurations afin d'améliorer la couverture des fonctions essentielles. Deux formes courantes de couverture de test sont la couverture des instructions (ou lignes) et la couverture des branches (ou arêtes). La couverture des lignes indique l'empreinte d'exécution du test, c'est-à-dire les lignes de code exécutées pour le mener à bien. La couverture des arêtes indique les branches ou points de décision du code exécutés pour mener à bien le test. Ces deux types de couverture fournissent une mesure, exprimée en pourcentage. La signification de ce pourcentage dépend de la ou des formes de couverture utilisées : une couverture de branches de 67 % est plus exhaustive qu'une couverture d'instructions de 67 %.
En général, les outils de couverture de test génèrent des calculs et une journalisation supplémentaires par rapport au programme lui-même, ce qui ralentit l'application. C'est pourquoi cette analyse n'est généralement pas effectuée en production. Comme on peut s'y attendre, certaines catégories de logiciels ne peuvent pas être soumises de manière réaliste à ces tests de couverture, même s'il est possible d'obtenir une approximation de la couverture par l'analyse plutôt que par des tests directs.
Certains types de défauts sont également affectés par ces outils. En particulier, certaines conditions de concurrence ou opérations sensibles au temps réel peuvent être masquées lors de l'exécution dans des environnements de test ; toutefois, inversement, certains de ces défauts peuvent devenir plus faciles à détecter en raison de la surcharge supplémentaire induite par le code de test.
La plupart des développeurs de logiciels professionnels utilisent les couvertures C1 et C2. C1 correspond à la couverture des instructions et C2 à la couverture des branches ou des conditions. La combinaison de C1 et C2 permet de couvrir la quasi-totalité des instructions d'un code. La couverture des instructions englobe également la couverture des fonctions (entrées et sorties), des boucles, des chemins d'exécution, des flux d'état, des flux de contrôle et des flux de données. Grâce à ces méthodes, il est possible d'atteindre une couverture de code proche de 100 % dans la plupart des projets logiciels.
Outils de couverture de code notables
Fabricants de matériel
Logiciel
Trèfle - Partenaire de développement Java
- EMMA
- Jtest
- Banc d'essai LDRA
Utilisation dans l'industrie
La couverture des tests est un élément à prendre en compte dans la certification de sécurité des équipements avioniques. Les directives selon lesquelles les équipements avioniques sont certifiés par la Federal Aviation Administration (FAA) sont documentées dans les normes DO-178B et DO-178C .
La couverture des tests est également une exigence de la partie 6 de la norme de sécurité automobile ISO 26262 Véhicules routiers - Sécurité fonctionnelle .
- La couverture fonctionnelle sera assurée si, au cours de cette exécution, la fonction