Vers une couverture complète des tests de régression

Par Parasoft

7 septembre 2011

3  min lire

Aussi surprenant que cela puisse paraître, même complet couverture de chemin ne signifie pas que votre code se comporte toujours correctement.

Si vous utilisez une approche de développement pilotée par les tests (TDD), vous serez familiarisé avec l'idée d'utiliser les tests unitaires comme une sorte de spécification pour le code testé. Vous écrivez d'abord un test qui affirme ce qu'une nouvelle méthode est censée faire, regardez-la échouer et ajoutez juste assez de code d'implémentation pour la faire réussir. Si vous montrez le cas de test de la liste 4 à un développeur et demandez une implémentation, le résultat pourrait être une méthode qui renvoie toujours 0, comme indiqué dans la liste 5. Fait intéressant, le développeur ne violerait même pas l'esprit de TDD en le faisant. . Il y aurait une couverture de chemin à 100% et aucun échec de test, mais évidemment la mise en œuvre ne fonctionnerait pas correctement. Dans la vraie vie, ces types de problèmes apparaissent rarement sous la forme de méthodes simples qui ajoutent deux valeurs, mais prennent généralement une forme plus compliquée. Le problème fondamental est qu'un ensemble de tests unitaires peut fournir une couverture complète du chemin pour une méthode, mais en même temps peut ne pas fournir une spécification complète de cette méthode. Un scénario typique est qu'un développeur optimise la mise en œuvre d'une méthode existante de telle sorte que tous les cas de test réussissent toujours, mais que des fonctionnalités précédemment non revendiquées sont supprimées ou modifiées par inadvertance. Cela peut se produire même avec une couverture de chemin à 100%.

L'exemple de code illustratif du listing 2 nécessiterait 256 cas de test différents (pour toutes les valeurs possibles de l'octet de type) pour atteindre la pleine les tests de régression couverture. Sans les cas de test supplémentaires, un développeur pourrait remplacer l'implémentation d'origine par un code simplifié qui renvoie des résultats corrects uniquement pour les entrées de test 0, 1, 2 et 3, mais lève simplement une exception ¬Opération¬Exception non prise en charge pour toutes les autres entrées. Tous les cas de test existants réussissent, mais une partie de la fonctionnalité d'origine est perdue. Même un ensemble de tests unitaires qui fournit une couverture complète du chemin n'est pas garanti pour détecter une telle régression.

Si la couverture de chemin est déjà difficile à réaliser, la couverture de régression complète ne peut être qualifiée que d'une impossibilité pratique. La méthode add du Listing 4 prend deux paramètres entiers, chacun pouvant prendre 232 valeurs différentes. Une couverture de régression complète pour cette méthode nécessiterait 264 cas de test (c'est-à-dire 232 × 232). Avec une approche de test paramétrée intelligente, il serait possible de représenter tous ces cas de test de manière compacte, mais il serait impossible d'exécuter tous les tests dans un délai raisonnable. Une couverture de régression complète détecterait même des problèmes subtils comme le tristement célèbre bogue Pentium FDIV; malheureusement, il est totalement irréalisable à toutes fins utiles.

Ne pas garantir le bon fonctionnement de quelque chose d'aussi simple qu'une méthode d'ajout n'est pas vraiment une source de confiance. Donc, à moins de créer des cas de test qui utilisent toutes les combinaisons possibles d'entrées, que pouvez-vous faire en pratique pour atteindre un niveau raisonnable de protection contre les échecs de régression? De toute évidence, plus vous continuez à tirer des entrées différentes sur une méthode testée, meilleure sera votre couverture de régression. La question est de savoir quelles entrées choisir pour vos cas de test. Les valeurs extrêmes (ou ce que l'on appelle les «cas d'angle») sont un bon choix. Les entrées de test pour un paramètre int pourraient, par exemple, utiliser Integer.MAX_VALUE, Integer.MAX_VALUE-1, 1, 0, -1, Integer.MIN_VALUE + 1 et Integer.MIN_VALUE comme entrées de test. De même, une référence nulle, une chaîne vide et diverses chaînes contenant des caractères spéciaux constituent de bonnes entrées de test pour un paramètre String.

Les cas de test qui visent à améliorer la couverture de régression ont souvent une structure de test commune, mais utilisent des valeurs d'entrée différentes. Dans ces situations, il peut être utile d'utiliser des outils qui prennent en charge le paramétrage des cas de test, qui extrait la structure de test commune et sépare les données de test dans une feuille de calcul Excel ou un autre magasin de données externe. Une autre option pour améliorer la couverture de régression d'une suite de tests est le test dit de perturbation, qui applique des modifications mineures (comme l'ajout ou la soustraction d'un petit nombre) aux valeurs d'entrée des tests existants dans le but d'exposer de nouveaux chemins de code intéressants et d'affirmer précédemment comportement non affirmé. Les valeurs de cas d'angle et les tests de perturbation sont heuristiques plutôt que systématiques, mais les deux peuvent encore augmenter la couverture de régression et peuvent être appliqués manuellement ou par des outils automatisés tels que Parasoft Jtest.

Le tableau 1 fournit un résumé des cas de test nécessaires pour obtenir une couverture de 100% des instructions, des branches, des chemins et des régressions complètes pour la méthode testée de la liste 2.


Tableau 1: Tester les entrées pour obtenir une couverture à 100% du listing 2

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.