Webinaire en vedette : Tests d'API améliorés par l'IA : une approche de test sans code | Visionnez maintenant
Un début essentiel pour fournir des services de haute qualité en temps réel logiciel embarqué Systems intègre l’analyse statique dans le flux de travail de développement. Grâce à l'analyse statique, les ingénieurs logiciels peuvent améliorer la fiabilité, les performances et la sécurité de leurs logiciels. Dans le même temps, ils peuvent réduire les coûts et le temps associés à l’identification et à la correction des défauts plus tard dans le cycle de développement.
L'analyse statique identifie les erreurs dans le code à un stade précoce du processus de développement, bien avant que le logiciel ne s'exécute sur le matériel cible. Cela inclut la détection des erreurs de syntaxe, des erreurs logiques et des erreurs d'exécution potentielles. La sécurité est également primordiale dans les systèmes embarqués, en particulier ceux qui font partie d'une infrastructure critique ou des appareils Internet des objets (IoT). L'analyse statique peut révéler des vulnérabilités telles que des dépassements de tampon, des failles de validation des entrées et d'autres failles de sécurité qui pourraient être exploitées par des attaquants.
De plus, la plupart des équipes de développement conviendront que les tests unitaires sont également essentiels au développement de logiciels embarqués, malgré les efforts et les coûts. Les tests unitaires aident les développeurs à vraiment comprendre le code qu'ils développent et fournissent une base solide à un régime de vérification et de validation nécessaire pour satisfaire les objectifs de sûreté et de sécurité d'un produit. S'appuyer sur cette base de tests unitaires permet aux équipes d'accélérer le développement agile tout en atténuant le risque que des défauts se glissent dans les étapes ultérieures du pipeline.
Pourquoi des tests automatisés ?
Analyse statique
L'automatisation de l'analyse statique dans le flux de travail de développement logiciel offre des avantages significatifs.
- S'intègre aux IDE de développeur et aux pipelines CI/CD. L'intégration de l'analyse statique dans le processus de construction pour l'ensemble de l'organisation de développement est une bonne pratique. Grâce à l'automatisation, les équipes peuvent intégrer l'analyse statique dans les IDE des développeurs pour une analyse rapide au fur et à mesure qu'ils écrivent le code. Lorsque les équipes intègrent l'analyse statique dans les pipelines CI/CD, le code est automatiquement analysé chaque fois qu'elles soumettent des modifications de code.
Cela garantit que les problèmes sont détectés et résolus immédiatement, tout en maintenant la qualité du code tout au long du processus de développement, ce qui est essentiel pour les grandes équipes ou les projets avec des cycles de développement rapides. Il appliquera les normes de codage sur l’ensemble de la base de code, éliminant ainsi les erreurs humaines et les biais dans les révisions de code. Cette approche proactive garantira également la sécurité en identifiant les vulnérabilités potentielles à atténuer avant qu'elles puissent être exploitées.
- Garantit une conformité continue. L'analyse statique automatisée garantit une conformité continue aux normes pertinentes telles que MISRA et CERT pour industries pour les tests de logiciels embarqués tels que l'automobile, les dispositifs médicaux et l'aérospatiale avec des exigences réglementaires strictes. Ceci est crucial pour réussir les audits et les certifications qui nécessitent une analyse statique.
- Élimine les tâches répétitives et banales. Les développeurs n'ont pas à effectuer de tâches répétitives et banales consistant à examiner le code et à attribuer les violations de codage identifiées à corriger. Parasoft applique des solutions brevetées d'IA et de ML au flux de travail d'analyse statique pour hiérarchiser les résultats de violation des règles et rationaliser les étapes de remédiation, réduisant immédiatement les efforts de l'équipe dans l'adoption de l'analyse statique. Les développeurs peuvent se concentrer sur des aspects plus complexes et créatifs du développement de logiciels, ce qui entraîne une satisfaction professionnelle et une productivité accrues.
Tests unitaires
Alors que l'analyse statique fournit des informations précieuses sur la qualité du code et les problèmes potentiels sans exécution, les tests unitaires vérifient le comportement réel et l'exactitude du code en cours d'exécution. L’utilisation conjointe des deux techniques garantit un processus de développement logiciel plus robuste et plus fiable, car elles se complètent et couvrent un plus large éventail de problèmes potentiels.
Cependant, malgré les avantages, les équipes de développement ont souvent du mal à effectuer suffisamment de tests unitaires. Les contraintes sur le nombre de tests sont dues à de multiples facteurs tels que la pression pour fournir rapidement des fonctionnalités accrues et la complexité et le temps nécessaire à la création de tests unitaires précieux.
Les raisons courantes citées par les développeurs qui limitent l'efficacité des tests unitaires en tant que pratique de développement de base sont les suivantes.
- Il est difficile de comprendre, d'initialiser et/ou d'isoler les dépendances de l'unité testée.
- Déterminer ce qu'il faut valider et définir les assertions appropriées prend du temps et nécessite souvent un travail de conjecture intelligent.
- Cela implique beaucoup de codage manuel, souvent même plus que ce qui est nécessaire pour implémenter une fonctionnalité ou une amélioration spécifique.
- Ce n'est tout simplement pas si intéressant. Les développeurs ne veulent pas se sentir comme des testeurs. Ils veulent consacrer du temps à fournir davantage de fonctionnalités.
Les outils d'automatisation des tests unitaires prennent universellement en charge une sorte de cadre de test, qui fournit l'infrastructure de harnais nécessaire pour exécuter les unités de manière isolée tout en satisfaisant les dépendances via des stubs. Cela inclut la génération automatisée d'ensembles de tests et les composants exécutables nécessaires aux tests basés sur l'hôte et la cible.
Cependant, la génération et la gestion des données de test constituent le plus grand défi en matière de tests unitaires et de génération de tests. Les cas de test doivent couvrir une gamme de rôles de validation tels que garantir les exigences fonctionnelles, détecter les comportements imprévisibles et garantir la sécurité et les exigences de sûreté. Le tout en satisfaisant aux critères de couverture des tests.
La génération automatisée de tests réduit les inefficacités des tests unitaires en supprimant les difficultés liées à l'initialisation, à l'isolement et à la gestion des dépendances. Il supprime également une grande partie du codage manuel requis tout en aidant à gérer les données de test nécessaires à la vérification et à la validation.
Analyse statique dans les systèmes embarqués
L'analyse du code statique fournit une variété de mesures qui aident à évaluer différents aspects de la sûreté, de la sécurité, de la fiabilité, des performances et de la maintenabilité du code embarqué. Ils offrent chacun des informations précieuses sur divers aspects de la santé du code. L'une de ces mesures est la complexité cyclomatique, qui mesure le nombre de chemins linéairement indépendants à travers le code source d'un programme.
Une complexité plus élevée indique un potentiel de bogues plus élevé et plus difficile à lire, à maintenir et à tester. D'autres valeurs que l'analyse statique peut fournir, qui ne constituent pas des mesures de qualité en elles-mêmes, mais donnent un sentiment de maintenabilité, incluent :
- Nombre total de lignes de code
- Densité des commentaires
- Couplage ou cohésion de classe
- Longueur de la fonction
Les mesures intéressantes que l'analyse statique peut fournir mais qui ne sont généralement pas appliquées incluent la couverture du code et les mesures de performances. Bien que la couverture du code soit généralement capturée par les tests unitaires, l'analyse statique peut fournir cette métrique et même détecter le code mort. La métrique de performances est mesurée en identifiant le code susceptible d'entraîner des problèmes de performances, tels que des boucles inefficaces ou des appels récursifs. Il existe de nombreuses autres mesures concernant l'utilisation des ressources, la duplication, le pointeur nul et la division par zéro.
En tirant parti de ces métriques fournies, l'analyse statique du code aide les développeurs à identifier les problèmes potentiels dès le début du cycle de développement, à améliorer la qualité et la maintenabilité du code et à garantir le respect des normes de codage et des meilleures pratiques.
Tests unitaires dans les systèmes embarqués
La vérification et la validation des logiciels font partie intégrante du développement de logiciels embarqués, et les tests constituent un moyen essentiel de démontrer le comportement correct des logiciels. Les tests unitaires sont la vérification de la conception du module. Il garantit que chaque unité logicielle fait ce qu'elle est censée faire.
De plus, les exigences de sûreté et de sécurité peuvent exiger que les unités logicielles ne se comportent pas de manière inattendue et ne soient pas susceptibles d'être manipulées par des entrées de données inattendues.
Dans le cadre du modèle de développement classique en V, l'exécution de tests unitaires est une pratique de validation permettant de garantir que la conception du module est correcte. De nombreuses normes de développement spécifiques à la sécurité contiennent des lignes directrices sur ce qui doit être testé pour les tests unitaires. Par exemple, la norme ISO 61502 et les normes associées comportent des directives spécifiques pour les tests conformément au niveau d'intégrité de sécurité, où les tests basés sur les exigences et les tests d'interface sont fortement recommandés pour tous les niveaux. Les tests d’injection de fautes et d’utilisation des ressources sont recommandés aux niveaux d’intégrité inférieurs et fortement recommandés aux niveaux SIL (Safety Integrity Levels) les plus élevés. De même, la méthode de réalisation des cas d'examen de conduite est également précisée avec des pratiques recommandées.
Exécution de test automatisée
L'automatisation des tests offre de grands avantages aux logiciels embarqués. S'éloigner des suites de tests qui nécessitent de nombreuses interventions manuelles signifie que les tests peuvent être effectués plus rapidement, plus facilement et plus souvent. Le déchargement de cet effort de test manuel libère du temps pour une meilleure couverture des tests et d’autres objectifs de sécurité et de qualité. Une exigence importante pour l’exécution automatisée d’une suite de tests est de pouvoir exécuter ces tests sur les environnements hôte et cible.
Tests basés sur des cibles pour les systèmes embarqués
L'automatisation des tests pour les logiciels embarqués est plus difficile en raison de la complexité du lancement et de l'observation des tests sur le matériel cible. Sans parler de l’accès limité au matériel cible dont disposent les équipes logicielles.
L'automatisation des tests logiciels est essentielle pour rendre les tests intégrés réalisables de manière continue, du système de développement hôte au système cible. Tester les logiciels embarqués est particulièrement chronophage. L'automatisation de la suite de tests de régression permet des économies considérables de temps et d'argent. De plus, les résultats des tests et la collecte de données de couverture de code à partir du système cible sont essentiels à la validation et à la conformité aux normes.
La traçabilité entre les cas de test, les résultats des tests, le code source et les exigences doit être enregistrée et maintenue. La collecte de données est donc essentielle à l’exécution des tests.
Couverture du code structurel
La collecte et l'analyse des métriques de couverture de code constituent un aspect important du développement de logiciels critiques pour la sécurité. La couverture du code mesure l'achèvement des scénarios de test et des tests exécutés. Il fournit la preuve que la validation est complète, au moins comme spécifié par la conception du logiciel. Il identifie également le code mort. Il s’agit d’un code qui ne pourra logiquement jamais être atteint. Cela démontre l’absence de comportement involontaire. Un code qui n'est couvert par aucun test constitue un handicap car son comportement et ses fonctionnalités sont inconnus.
La quantité et l'étendue de la couverture du code dépendent du niveau d'intégrité de sécurité. Plus le niveau d’intégrité est élevé, plus la rigueur utilisée est élevée, et inévitablement le nombre et la complexité des cas de tests. Quel que soit le niveau de couverture requis, la génération automatisée de cas de test peut augmenter la couverture des tests au fil du temps.
Les outils avancés d’automatisation des tests unitaires devraient mesurer ces métriques de couverture de code. De plus, il est nécessaire que cette collecte de données fonctionne sur les tests hôtes et cibles et accumule l'historique de couverture des tests au fil du temps. Cet historique de couverture de code peut couvrir les tests unitaires, d'intégration et de système pour garantir que la couverture est complète et traçable à tous les niveaux de tests.
Types de génération automatisée de cas de test
Pour des raisons pratiques, les outils automatisés doivent générer des cas de test dans des formats existants bien connus comme CppUnit. Par défaut, une suite de tests par fichier source/en-tête est logique, mais les outils doivent prendre en charge une suite de tests par fonction ou une suite de tests par fichier source si nécessaire.
Une autre considération importante concerne les définitions automatiques de stub pour remplacer les fonctions « dangereuses », qui incluent les routines d'E/S système telles que rmdir(), remove(), rename(), etc. De plus, des stubs peuvent être générés automatiquement pour les définitions de fonctions et de variables manquantes. Des talons définis par l'utilisateur peuvent être ajoutés selon les besoins.
Génération de cas de test basée sur les exigences
Bien que les outils d'automatisation des tests ne puissent pas dériver les tests d'exigences de la documentation, ils peuvent contribuer à rendre la création de scénarios de test, de stubs et de simulations plus facile et plus efficace. De plus, l'automatisation améliore considérablement la gestion des données des scénarios de test et la prise en charge des outils pour les tests paramétrés réduit également l'effort manuel.
La traçabilité depuis les exigences jusqu'au code, en passant par les tests et les résultats des tests, est particulièrement importante. La gestion manuelle de la traçabilité est presque impossible et l'automatisation fait de la double traçabilité une réalité.
Pendant que les exigences sont décomposées, la traçabilité doit être maintenue tout au long des phases de développement, car les exigences des clients se décomposent en exigences système, de haut niveau et de bas niveau. La phase de codage ou de mise en œuvre réalise les exigences de bas niveau. Considérez le diagramme en V typique du logiciel.
Chaque phase entraîne la phase suivante. À leur tour, les éléments de travail ou les exigences affinées de chaque phase doivent satisfaire aux exigences de la phase précédente. Les exigences architecturales qui ont été créées ou décomposées à partir de la conception du système doivent satisfaire la conception/les exigences du système, et ainsi de suite.
La traçabilité prouve que chaque phase satisfait aux exigences de chaque phase suivante.
Les développeurs écrivent du code qui implémente ou réalise chaque exigence et pour les applications critiques en matière de sécurité, des liens de traçabilité jusqu'aux cas de test et jusqu'au code sont établis. Par conséquent, si une exigence client change ou est supprimée, l’équipe sait quel impact cela aura sur toute la ligne, jusqu’au code et aux tests qui valident les exigences.
Les normes industrielles telles que DO-178B/C, ISO 26262, CEI 62304, CEI 61508, EN 50128 et d'autres exigent la construction d'une matrice de traçabilité pour l'identification de toute lacune dans la conception et la vérification des exigences. Cela permet d’atteindre l’objectif ultime de créer le bon produit. Plus que cela, il s'agit de garantir que le produit présente la qualité, la sûreté et la sécurité nécessaires pour garantir qu'il reste le bon produit.
Génération de cas de test basée sur la couverture de code
La création de tests unitaires productifs a toujours été un défi. La conformité aux normes de sécurité fonctionnelle exige des logiciels de haute qualité, ce qui entraîne un besoin de suites de tests qui affectent et produisent des statistiques de couverture de code élevées. Les équipes ont besoin de cas de tests unitaires qui les aident à atteindre une couverture de code à 100 %. C'est plus facile à dire qu'à faire. Analyser les branches du code et essayer de trouver les raisons pour lesquelles certaines sections de code ne sont pas couvertes continue de voler des cycles aux équipes de développement.
Les outils d’automatisation des tests unitaires peuvent être utilisés pour combler les lacunes de couverture dans les suites de tests. Par exemple, une analyse avancée du code statique (analyse des flux de données et de contrôle) est utilisée pour trouver des valeurs pour les paramètres d'entrée requis pour exécuter des lignes spécifiques de code non couvert.
Il est également utile que vous disposiez d'outils automatisés qui non seulement mesurent la couverture du code, mais suivent également la quantité de code modifié couverte par les tests, car cela peut permettre de savoir si suffisamment de tests sont en cours d'écriture ainsi que les modifications apportées au code de production. Consultez l’exemple de rapport de couverture de code suivant.
Utiliser l'analyse de code pour piloter des cas de test basés sur la couverture
Dans un code complexe, il y a toujours des instructions de code insaisissables dont il est extrêmement difficile d'obtenir une couverture. Il est probable qu'il existe plusieurs valeurs d'entrée avec diverses permutations et chemins possibles qui rendent le déchiffrement compliqué et long. Mais une seule combinaison peut vous offrir la couverture dont vous avez besoin. La combinaison de l'automatisation des tests et de l'analyse statique permet d'obtenir facilement une couverture des lignes de code difficiles à atteindre. Un exemple de conditions préalables de test calculées avec l’analyse du code est présenté dans Coverage Advisor.
Génération de cas de test de défauts
Une autre classe de tests est celle créée pour induire une condition d'erreur dans l'unité testée. Dans ces cas, les paramètres d'entrée sont souvent hors limites et se situent juste aux conditions limites pour les types de données, comme l'utilisation des entiers positifs et négatifs de 32 bits les plus élevés pour les données de test. D'autres exemples sont les tests fuzz dans lesquels ces conditions limites sont mélangées à des données aléatoires conçues pour créer une condition d'erreur ou déclencher une vulnérabilité de sécurité.
Ces cas de test valident les exigences non fonctionnelles car elles ne relèvent pas du champ d'application des exigences du produit, mais sont essentielles pour déterminer les performances, la sécurité, la sûreté, la fiabilité et d'autres qualités du produit. L'automatisation est essentielle puisque ces tests peuvent être nombreux (fuzz testing) et s'appuyer sur des exécutions répétées (tests de performances) pour permettre de découvrir des problèmes de qualité. La génération de cas de test permet de réduire l'effort manuel nécessaire à la création de ces suites de tests.
Les tests de régression
Dans le cadre de la plupart des processus de développement de logiciels, des tests de régression sont effectués après que des modifications ont été apportées au logiciel. Ces tests déterminent si les nouvelles modifications ont eu un impact sur le fonctionnement existant du logiciel. La gestion et l'exécution des tests de régression représentent une grande partie des efforts et des coûts liés aux tests. Même avec la génération automatisée de tests, l’exécution des tests, la collecte des résultats et la réexécution des tests prennent beaucoup de temps. Les tests de régression englobent la maintenance des scénarios de test, l'amélioration de la couverture du code et la traçabilité.
Les tests de régression sont nécessaires, mais ils indiquent seulement que les récentes modifications du code n'ont pas provoqué l'échec des tests. Rien ne garantit que ces changements fonctionneront. En outre, la nature des changements qui justifient la nécessité d'effectuer des tests de régression peut aller au-delà de l'application actuelle et inclure des modifications du matériel, du système d'exploitation et de l'environnement d'exploitation.
En fait, tous les cas de test créés précédemment devront peut-être être exécutés pour garantir qu'aucune régression n'existe et qu'une nouvelle version logicielle fiable soit construite. Ceci est essentiel car chaque nouvelle version de système logiciel ou de sous-système est construite ou développée. Si vous n’avez pas de fondations solides, tout peut s’effondrer.
Pour éviter cela, il est important de créer des références de tests de régression qui constituent un ensemble organisé de tests et vérifieront automatiquement tous les résultats. Ces tests sont exécutés automatiquement et régulièrement pour vérifier si les modifications du code modifient ou interrompent les fonctionnalités capturées dans les tests de régression. Si des changements sont introduits, ces cas de test ne parviendront pas à alerter l'équipe du problème. Lors des tests suivants, Parasoft C++test signalera les tâches s'il détecte des changements dans le comportement capturés lors du test initial.
Comment décider quoi tester
Le principal défi des tests de régression consiste à déterminer quelles parties d’une application tester. Il est courant d'exécuter par défaut tous les tests de régression en cas de doute sur l'impact des récentes modifications de code : l'approche tout ou rien.
Pour les grands projets logiciels, cela devient une entreprise énorme et réduit la productivité de l'équipe. Cette incapacité à cibler les tests entrave une grande partie des avantages des processus itératifs et continus, potentiellement exacerbés dans les logiciels embarqués où les cibles de test constituent une ressource limitée.
Quelques tâches sont requises ici.
- Identifiez les tests qui doivent être réexécutés.
- Concentrez les efforts de test (tests unitaires, tests fonctionnels automatisés et tests manuels) sur la validation des fonctionnalités et du code associé impactés par les modifications les plus récentes.
Analyse d'impact des tests
L'analyse d'impact des tests (TIA) utilise les données collectées lors des exécutions de tests et les modifications du code entre les versions pour déterminer quels fichiers ont été modifiés et quels tests spécifiques ont touché ces fichiers. Le moteur d'analyse de Parasoft peut analyser le delta entre deux builds et identifier le sous-ensemble de tests de régression qui doivent être exécutés. Il comprend également les dépendances sur les unités modifiées pour déterminer l'effet d'entraînement que les modifications ont eu sur d'autres unités.
Concentrez-vous sur le risque
En raison de la complexité des bases de code actuelles, chaque changement de code, aussi inoffensif soit-il, peut avoir un impact subtil sur la stabilité des applications et finalement « casser le système ». Ces conséquences involontaires sont impossibles à découvrir par une inspection manuelle. Les tests sont donc essentiels pour atténuer le risque qu'elles représentent. À moins que l'on comprenne ce qui doit être reposé, une pratique de test efficace ne peut pas être réalisée. S'il y a trop de tests dans chaque sprint ou itération, l'efficacité apportée par l'automatisation des tests est réduite. Trop peu de tests n’est pas une option.
La meilleure approche consiste à identifier les tests qui doivent être réexécutés et à concentrer les efforts de test (tests unitaires, tests fonctionnels automatisés et tests manuels) sur la validation des fonctionnalités et du code associé qui sont impactés par les modifications les plus récentes.
Ceci est découvert avec TIA et les tests de planification basés sur une approche basée sur les données appelée test basé sur le changement.
TIA a besoin d'un référentiel de tests déjà terminés et déjà exécutés pour chaque build, soit dans le cadre d'un processus de test entièrement automatisé (comme une étape de build pilotée par CI), soit lors du test de la nouvelle fonctionnalité. Cette analyse fournit un aperçu de l'endroit où les modifications ont eu lieu dans le code, de la manière dont les tests existants sont corrélés à ces modifications et des domaines sur lesquels les ressources de test doivent se concentrer. Voici un exemple de TIA.
À partir de là, le plan de test de régression est augmenté pour traiter les cas de test ayant échoué et incomplets avec la priorité la plus élevée et en utilisant les recommandations de retest pour concentrer la planification d'exécutions automatisées supplémentaires et la priorisation des efforts de tests manuels.
Les tests constituent un goulot d'étranglement majeur pour le développement de logiciels embarqués, car trop de défauts sont identifiés à la fin du cycle de publication en raison de tests insuffisants ou mal orientés. Pour obtenir les meilleurs résultats, concentrez vos efforts de test sur l’impact des modifications apportées afin de libérer l’efficacité offerte par l’automatisation des tests.
Smiths Medical utilise la génération automatisée de tests pour une prestation sûre et de haute qualité
Conformité satisfaite
Satisfait aux exigences d’analyse statique, de tests unitaires et de couverture de code.
Simplifiée
Qualification d'outils.
Résumé
Les tests sont essentiels au développement de logiciels embarqués. Il favorise une véritable compréhension du code en cours de développement et fournit une base solide à un régime de vérification et de validation nécessaire pour satisfaire les objectifs de sûreté et de sécurité d'un produit.
L'analyse statique est une technique cruciale dans le développement de logiciels qui consiste à examiner le code source sans l'exécuter pour identifier les problèmes potentiels et améliorer la qualité globale du code. Lorsque les équipes l’utilisent dès le départ, l’analyse statique :
- Fournit des informations précieuses sur la complexité du code, la maintenabilité, les vulnérabilités de sécurité et le respect des normes de codage.
- Aide à prévenir les bugs.
- Optimise les performances.
- Assure le respect des bonnes pratiques et des exigences réglementaires.
L'intégration de l'analyse statique dans le flux de travail de développement conduit à une base de code plus robuste et plus maintenable. Cela doit être effectué en conjonction avec des tests unitaires afin de ne pas être considéré comme un frein à la productivité.
Les contraintes sur la productivité des tests sont dues à de multiples facteurs tels que la pression et le temps nécessaires pour fournir des fonctionnalités accrues, ainsi que la complexité et le temps nécessaire à la création de tests précieux.
La génération et la gestion des données de test constituent de loin le plus grand défi en matière de tests unitaires et de génération de tests. Les cas de test sont particulièrement importants dans le développement de logiciels critiques pour la sécurité, car ils doivent garantir les exigences fonctionnelles et tester les comportements imprévisibles, ainsi que les exigences de sécurité et de sûreté. Le tout en satisfaisant aux critères de couverture des tests.
La génération automatisée de tests réduit les inefficacités des tests unitaires en supprimant les difficultés liées à l'initialisation, à l'isolation et à la gestion des dépendances. Il supprime également une grande partie du codage manuel requis tout en aidant à gérer les données de test nécessaires à la vérification et à la validation. Cela améliore la qualité, la sûreté et la sécurité. Cela réduit également la durée des tests, les coûts et les délais de commercialisation.