Densité et chevauchement des tests pour la couverture du code

Par Parasoft

14 septembre 2011

4  min lire

Le concept de densité de couverture est quelque peu lié au chevauchement des tests. En règle générale, vous voulez éviter que plusieurs cas de test testent la même fonctionnalité (c'est-à-dire minimiser le chevauchement des tests).

L'idée derrière ce principe est que les cas de test ne se cassent pas seulement à cause de bogues dans le code, mais plus fréquemment à cause de changements dans la spécification. Le comportement du code qui était auparavant supposé correct peut devenir incorrect ou insuffisant en raison de nouvelles exigences. Si le code est modifié pour refléter la nouvelle exigence, les cas de test qui affirment l'ancien comportement peuvent être interrompus et doivent être mis à jour. Si vous suivez une approche TDD, la mise à jour des cas de test doit avoir lieu avant que le code testé ne soit modifié. Dans une suite de tests avec un niveau élevé de chevauchement de tests, un petit changement dans la spécification (et l'implémentation) peut nécessiter la mise à jour d'un grand nombre de cas de test. Dans une telle situation, les cas de test se chevauchaient manifestement dans certains détails de code susceptibles de changer. De telles occurrences sont hautement indésirables car elles augmentent considérablement le coût de maintenance de la suite de tests.

Classes d'équivalence

La minimisation du chevauchement des tests est étroitement liée au concept mathématique d'une soi-disant classe d'équivalence. En termes mathématiques simplifiés, une classe d'équivalence est un ensemble d'éléments qui sont tous équivalents les uns aux autres selon une certaine relation. Cette idée est également pertinente pour la couverture des tests.

Nous avons précédemment identifié quatre chemins de code différents pour la méthode testée illustrée dans le Listing 2. Si vous regardez de plus près la logique du code, vous remarquerez que seuls les deux bits les moins significatifs de l'entrée sont évalués. Les bits restants de l'entrée n'ont aucune influence sur le chemin de code emprunté. Donc, effectivement, tester la méthode avec une valeur d'entrée de 4 utilisera le même chemin de code que celui pris pour une valeur d'entrée de 0. De la même manière, les valeurs d'entrée 1, 5, 9, 13,… provoqueront toutes le même chemin de code à prendre. Aux fins de la couverture de chemin, la méthode du listing 2 a quatre classes d'équivalence, qui sont résumées dans le tableau 2.

Tableau 2: Classes d'équivalence pour la couverture de chemin du listing 2

Pour obtenir une couverture de chemin complète avec un chevauchement de test minimal, il suffit de sélectionner une valeur d'entrée dans chaque classe d'équivalence. Il n'y a rien à gagner en termes de couverture si plusieurs cas de test utilisent des valeurs d'entrée de la même classe d'équivalence. Les classes d'équivalence varient en fonction des critères de couverture. Par exemple, en termes de couverture des instructions, les valeurs d'entrée 0 et 2 sont toutes deux dans la classe d'équivalence pour couvrir l'instruction de retour null de la méthode exemple, mais elles seraient dans des classes d'équivalence différentes lors de l'examen de la couverture de chemin.

L'identification des classes d'équivalence pour les entrées de test est un outil utile pour minimiser le chevauchement des tests, mais encore une fois, des problèmes se profilent lorsque nous nous dirigeons vers une couverture de régression complète. Si une suite de tests atteint une couverture de régression complète pour une méthode particulière, cela implique que la suite de tests forme une spécification complète de cette méthode. Tout changement dans le comportement de la méthode, aussi mineur soit-il, entraînerait un échec du test. Pour l'exemple de méthode du Listing 2, nous avons déjà déterminé que des cas de test avec les 256 valeurs d'entrée possibles seraient nécessaires. Combien de classes d'équivalence pour les entrées de test y aurait-il en termes de couverture de régression complète? Malheureusement, la réponse à cette question est «256».

Pour une couverture de régression complète, aucune valeur d'entrée n'est équivalente à une autre valeur d'entrée. Une couverture de régression complète signifie que le comportement d'une méthode est complètement «verrouillé». Par exemple, même si les valeurs d'entrée 0 et 4 sont dans la même classe d'équivalence aux fins de la couverture de chemin, elles sont dans leurs propres classes d'équivalence pour une couverture de régression complète. S'ils étaient dans la même classe d'équivalence, cela impliquerait que le simple fait de choisir l'une des valeurs (par exemple, 4) satisferait toujours le critère de couverture de régression complète. Cependant, dans ce cas, il serait possible d'implémenter la méthode testée de telle manière qu'elle fonctionne correctement pour l'entrée de 4 mais pas pour l'entrée de 0 (par exemple, en ajoutant une vérification qui renvoie délibérément un résultat erroné si l'entrée était 0). Par conséquent, 0 et 4 ne peuvent pas être dans la même classe d'équivalence pour une couverture de régression complète.

Densité de couverture de code

Encore une fois, lorsqu'on vise une couverture de régression complète, l'astuce consistant à ne sélectionner qu'une seule entrée de chaque classe d'équivalence ne peut plus être utilisée pour minimiser le chevauchement des tests. Une couverture de régression complète entraînera toujours un chevauchement supplémentaire. Quels autres principes peuvent être utilisés pour atténuer les effets négatifs du chevauchement des tests?

Un problème fréquent est le code commun qui est exécuté directement ou indirectement par un grand nombre de cas de test. Les modifications de spécification affectant ce code commun sont susceptibles de provoquer un grand nombre d'échecs. Le but est d'éviter de telles concentrations et de réécrire les tests de manière à limiter la quantité de code couramment exécuté. La densité de couverture est une métrique utile qui peut être utilisée pour créer des suites de tests qui exécutent le code testé de manière plus uniforme. La densité de couverture étend la dichotomie «couvert» par rapport «non couvert» à une métrique numérique qui compte également la fréquence à laquelle une branche ou un chemin est exécuté. Par exemple, au lieu d'obtenir simplement une réponse par oui ou par non pour savoir si une ligne particulière a été couverte, la densité de couverture vous indiquerait également que la ligne a été exécutée exactement 500 fois. La densité de couverture peut être appliquée à n'importe quel critère de couverture, mais le plus souvent elle est offerte en conjonction avec une couverture de relevé ou de succursale.

Là encore, la visualisation des densités de couverture de chemin est tout aussi problématique que la visualisation d'une simple couverture de chemin «oui / non». Une manière courante de visualiser la densité de couverture consiste à ajouter des marqueurs colorés avec une luminosité différente dans l'éditeur de code source. Par exemple, une nuance de vert clair pourrait indiquer qu'un morceau de code est couvert par quelques cas de test, mais une nuance de vert extrêmement sombre serait un avertissement qu'il y a une grande concentration de cas de test qui exécutent tous le même morceau particulier. du code. De tels indicateurs d'avertissement devraient idéalement provoquer une refactorisation qui déplace le code commun hors du chemin du code.

***

Crédit image: Jen et une caméra

Par Parasoft

Les outils de test de logiciels automatisés de pointe de Parasoft prennent en charge l'ensemble du processus de développement logiciel, depuis le moment où le développeur écrit la première ligne de code jusqu'aux tests unitaires et fonctionnels, jusqu'aux tests de performance et de sécurité, en exploitant des environnements de test simulés en cours de route.

Recevez les dernières nouvelles et ressources sur les tests de logiciels dans votre boîte de réception.