Table of Contents
Dans le développement de logiciels d'ingénierie, il est essentiel de garantir des tests complets pour la fiabilité, la sécurité et la conformité réglementaire. Les outils de couverture de code sont essentiels pour identifier les chemins non testés dans votre base de code, les séquences spécifiques de code qui ne s'exécutent jamais pendant votre suite de test. En découvrant systématiquement ces lacunes, les équipes d'ingénierie peuvent réduire les bogues cachés, améliorer la robustesse des logiciels et répondre à des normes industrielles strictes comme DO-178C (avionique) ou ISO 26262 (automotive).
Quels sont les outils de couverture du code?
Les outils de couverture de code sont des utilitaires logiciels qui surveillent les parties de votre code source qui sont exécutées lorsque vous exécutez votre suite de test. Ils fonctionnent en instrumentant le code — en insérant des sondes ou des compteurs — soit au moment de la compilation (pour les langues compilées) ou au moment de l'exécution (pour les langues interprétées).
Le but principal est de mesurer la rigueur des tests, mais les outils de couverture mettent aussi directement en évidence le code non testé. Lorsqu'une fonction, une branche conditionnelle ou un chemin logique n'est jamais visité, il apparaît tel qu'il est découvert dans le rapport.
Pour les logiciels d'ingénierie écrits en C/C++, des outils comme gcov et BullseyeCoverage[ sont courants. Pour les systèmes basés sur Java, JaCoCo est la norme de facto. Pour .NET, OpenCover[ ou Coverlet[ sont largement utilisés.
Types de couverture et leur importance
La couverture du code n'est pas une seule métrique, différents types de couverture révèlent différents aspects de l'exhaustivité des tests.
Couverture par ligne
La couverture de ligne (également appelée couverture de déclaration) mesure le pourcentage de lignes de code exécutables qui ont été exécutées pendant les tests. C'est la métrique la plus simple et souvent la plus largement signalée. Si une ligne n'est jamais exécutée, c'est un chemin évident non testé. Cependant, la couverture de ligne peut être trompeuse: un test peut exécuter chaque ligne mais manque toujours comportement dangereux parce qu'une branche conditionnelle n'a jamais été prise.
Couverture de la branche
La couverture de la succursale mesure si tous les résultats de décision possibles (vrai/faux pour , cas pour , sorties de boucle) ont été exercés. Dans C/C++ et Java, la couverture de la succursale est généralement exprimée en pourcentage de toutes les branches. Les branches non testées sont des chemins non testés directs qui peuvent masquer des erreurs logiques. Par exemple, une peut avoir la branche découverte, ce qui signifie que le chemin n'a jamais été exécuté dans aucun test.
Couverture du chemin
Pour une fonction à plusieurs conditions imbriquées, le nombre de chemins augmente de façon exponentielle (explosion du sentier).Dans la pratique, la couverture du sentier est souvent approximative en combinant la couverture de branche et de condition. Les normes critiques de sécurité comme DO-178C Niveau A peuvent exiger une couverture modifiée de condition/décision (MC/DC) comme substitut pratique, lorsque chaque condition dans une décision est montrée comme ayant une incidence indépendante sur le résultat.
Couverture de la condition (MC/DC)
MC/DC va plus loin en exigeant que chaque condition modifie de façon indépendante le résultat de la décision. C'est la forme la plus rigoureuse de couverture pour les logiciels d'ingénierie critiques en matière de sécurité et expose directement les chemins non testés par une logique complexe. Par exemple, dans un système de contrôle de vol avionique, l'analyse MC/DC pourrait révéler qu'une condition de défaillance spécifique du capteur ne provoque jamais un arrêt du système parce que la suite de test n'a jamais exercé cette combinaison d'entrées.
Pour les systèmes à haute intégrité, le recours à la couverture en ligne est dangereux; les branches et les chemins non testés peuvent entraîner des défaillances catastrophiques.
Pourquoi identifier des chemins non testés?
Les chemins non testés représentent des séquences de code qui n'ont jamais été validées. Dans les logiciels d'ingénierie — systèmes de contrôle intégrés, moteurs de simulation ou firmware de dispositifs médicaux — ces lacunes peuvent entraîner des défaillances qui entraînent des risques de sécurité, une dégradation des performances ou une non-conformité réglementaire.
- Therac-25 (1980s):[ Une radiothérapie a échoué en raison d'une condition de course dans son logiciel de contrôle qui n'avait jamais été testée sous certaines séquences opérationnelles. Le chemin de code qui a permis l'erreur a été découvert par des tests mais seulement après un accident mortel.
- Mars Climat Orbiter (1999): Un chemin de code de navigation qui mélangeait unités métriques et impériales n'a jamais été exercé dans les essais au sol.
- Toyota accélération involontaire (2009): Les chemins de code critiques dans l'ECU n'ont pas été testés dans des conditions réelles, ce qui a conduit à un rappel de millions de véhicules.
L'identification des chemins non testés avant la libération est une stratégie proactive d'atténuation des risques. Elle permet également de satisfaire les auditeurs réglementaires : les normes comme ISO 26262, DO-178C et CEI 62304 exigent une analyse de couverture structurelle dans le cadre du processus de vérification.
Utilisation des outils de couverture pour trouver des chemins non testés
Le travail pratique pour identifier les chemins non testés implique plusieurs étapes, en commençant par l'instrumentation et se terminant par la création de tests ciblés.
Instrumentation et exécution des essais
Pour gcov, compilez avec . Pour JaCoCo, utilisez l'agent via le drapeau . Exécutez votre suite de test complète. L'outil enregistre le code qui est frappé et écrit des fichiers de données brutes (p. ex. pour gcov, pour JaCoCo).
Générer et examiner des rapports sur la couverture
Utilisez la commande de reporting de l'outil (p. ex. , ) pour produire des rapports HTML ou XML. Ces rapports indiquent des lignes de code couleur (vert = frappé, rouge = non frappé) et des branches non identifiées. Concentrez-vous d'abord sur des fonctions ou des modules avec des pourcentages de couverture faibles. Pour chaque ligne ou branche non couverte, demandez : Ce code est-il accessible sous n'importe quelle condition ? Si oui, il représente un chemin non testé.
Analyser les voies non testées
Tous les chemins non testés ne sont pas aussi importants.
- Code de manipulation des errrors (p. ex., gestionnaires d'exception, routines de repli) — souvent laissés non testés mais critiques pour un fonctionnement sûr.
- ]Cas d'escroquerie et conditions de bordure[ — boucles qui n'initient jamais, indices de tableau aux limites, cas par défaut dans les instructions de commutation.
- Fonctions liées à la sécurité[ — code qui surveille les capteurs, actionne les sorties ou vérifie les invariants.
Utiliser les rapports de couverture pour identifier des séquences spécifiques : une branche rouge à l'intérieur d'un imbriqué indique un chemin qui ne se produit jamais dans aucun test. Ecrivez de nouveaux cas de test qui forcent cette condition à être vraie (ou fausse) en fournissant des données d'entrée appropriées.
Outils de couverture populaires pour les logiciels d'ingénierie
Choisir le bon outil dépend de votre langue, de votre plateforme et des exigences de couverture.
gcov (C/C++)
gcov est l'outil de couverture GNU livré avec GCC. Il fournit une couverture de ligne et de branche et est libre et open source. Il s'intègre bien avec des systèmes de construction comme CMake et peut être utilisé dans la compilation croisée pour des cibles intégrées. Documentation officielle gcov explique comment générer des rapports.
JACOCO (Java)
JaCoCo est la bibliothèque de couverture standard pour Java. Elle offre une couverture ligne, branche et méthode. Son agent peut se connecter à l'exécution de JVM sans changement de code. Pour les systèmes d'ingénierie construits sur Java (par exemple, SCADA ou cadres de simulation), JaCoCo est très efficace. JaCoCo website dispose de guides de configuration détaillés.
Couverture des yeux de taureaux (C/C++)
BullseyeCoverage est un outil commercial qui fournit une couverture de fonctions, de branches et de conditions (y compris MC/DC). Il est conçu pour le développement de sécurité critique et intégré. Ses rapports montrent exactement quelles conditions dans les expressions ne sont pas testées.
Autres outils
- OpenCppCoverage (Windows, C/C++) — un outil open-source gratuit qui s'intègre à Visual Studio et offre une couverture de branche.
- Coverage.py (Python) — pour les scripts d'ingénierie basés sur des données écrits en Python, cet outil fournit une couverture en ligne et en branche.
- La couverture intégrée de Go — Go fournit une couverture de ligne et de déclaration, avec un support de branche expérimental.
De nombreuses équipes utilisent également des services d'agrégation basés sur le cloud comme [Codecov ou SonarQube pour visualiser les tendances de couverture et les requêtes de tirage de porte.
Intégration de la couverture dans le flux de travail de développement
L'identification des chemins non testés devrait être une activité continue et non une vérification ponctuelle.
- Couverture de la course sur chaque commit — même une couverture partielle donne une rétroaction rapide.
- Établir des seuils de couverture minimum[ — construire une défaillance si la couverture tombe sous un niveau configurable (p. ex., couverture de 80 % pour les modules critiques).
- Générer les rapports de couverture comme artefacts — les rendre accessibles à tous les développeurs.
- Créer des diffs de couverture — des outils comme Codecov montrent quelles lignes une nouvelle requête de traction touches qui ne sont pas testées, forçant les développeurs à ajouter des tests pour les changements non identifiés.
- Gate fusionne sur des chemins non testés — pour les logiciels à haute intégrité, nécessite une couverture de 100% MC/DC pour les fonctions critiques en matière de sécurité avant la fusion.
L'automatisation supprime le fardeau de l'inspection manuelle. Les développeurs peuvent voir des chemins non testés surlignés dans leur éditeur ou dans le tableau de bord CI et écrire des tests immédiatement.
Meilleures pratiques pour une utilisation efficace
Pour tirer le meilleur parti des outils de couverture de code pour trouver des chemins non testés, suivez ces pratiques :
- Combinez plusieurs types de couverture[ — la couverture de ligne seule peut être trompeuse. Utilisez la couverture de branche et de condition pour découvrir des chemins plus profonds non testés.
- Focus sur le code à risque élevé[ — Cibler les fonctions complexes, les gestionnaires d'erreurs et les routines sensibles à la sécurité.
- Utilisez l'analyse statique à côté de la couverture[ — l'analyse statique peut trouver un code inaccessible que les outils de couverture peuvent manquer (p. ex., code mort non exécuté en raison d'erreurs logiques). Ensemble, ils fournissent une image plus complète.
- Couverture de mesure dans des conditions réalistes[ — utiliser des tests d'intégration et de niveau système, et non pas seulement des tests unitaires.
- Éviter la couverture pour le bien de la couverture[] — des tests d'écriture qui augmentent artificiellement la couverture sans valider le comportement (p. ex., tester des getters triviaux) gaspillent l'effort.
- Review coverage trends with time — une tendance de couverture décroissante indique que de nouveaux codes sont ajoutés sans tests correspondants, créant de nouveaux chemins non testés.
- Éduquer l'équipe — aider les développeurs à comprendre que les rapports de couverture ne sont pas un jugement, mais un outil pour trouver des lacunes.
Défis et limites
Bien que puissants, les outils de couverture de code ont des limites que les équipes doivent reconnaître :
- Overhead — l'instrumentation peut ralentir l'exécution des tests et augmenter la taille binaire. Pour les systèmes embarqués avec une mémoire serrée, cela peut être problématique.
- Faux degré de confiance — une couverture élevée ne signifie pas des tests parfaits. Les tests peuvent exercer le code mais ne pas vérifier correctement les résultats. Combiner la couverture avec des tests de densité d'affirmation et de mutation.
- Extension de la voie — pour un code très complexe avec de nombreuses conditions, la couverture de chemin est impossible à calculer.
- Instrument de production — La plupart des outils de couverture sont conçus pour les essais de développement. Le déploiement de code instrumenté à la production est risqué en raison de problèmes de performance et de sécurité.
- Limitations de la langue et de l'environnement[ — certaines cibles intégrées ne disposent pas d'outils de couverture robustes, surtout pour le montage ou le matériel personnalisé.
Malgré ces défis, la couverture de code reste l'un des moyens les plus efficaces pour identifier les chemins non testés. La clé est d'utiliser les outils intelligemment et de les combiner avec d'autres méthodes de vérification.
Conclusion
En révélant les lignes, les branches et les conditions non testées, ils fournissent une façon de concentrer les efforts de test là où ils comptent le plus. Lorsqu'ils sont intégrés aux flux de travail CI/CD et associés à une analyse statique et à une hiérarchisation fondée sur les risques, les analyses de couverture aident à prévenir les bogues cachés qui peuvent conduire à des défaillances dans le domaine. Que vous développiez un firmware avionique, des unités de contrôle automobile ou un logiciel de simulation industrielle, l'identification systématique des chemins non testés à l'aide d'outils de couverture devrait être un élément central de votre stratégie de vérification. Commencez par l'outil approprié pour votre langue, définissez des cibles de couverture réalistes et surveillez continuellement les progrès.