Embedded Software schneller testen Tests mit einem CI/CD-Workflow optimieren

Von Ricardo Camacho *

Firmen zum Thema

Die Implementierung eines CI / CD-Workflows (Continuous Integration and Continuous Delivery) für die Entwicklung eingebetteter Software wird immer beliebter. Gleichzeitig stellt allerdings das Durchführen von Tests für eingebettete Software in einer CI/CD-Umgebung eine gesteigerte Herausforderung dar. Effiziente Testautomatisierung ist darum ein Muss.
Die Implementierung eines CI / CD-Workflows (Continuous Integration and Continuous Delivery) für die Entwicklung eingebetteter Software wird immer beliebter. Gleichzeitig stellt allerdings das Durchführen von Tests für eingebettete Software in einer CI/CD-Umgebung eine gesteigerte Herausforderung dar. Effiziente Testautomatisierung ist darum ein Muss.
(Bild: gemeinfrei / Pixabay )

In der Embedded-Entwicklung sind Continuous Integration / Continuous Delivery (CI/CD) Workflows gang und gäbe. Einen Wasserfallprozess umzustellen auf CI/CD und agile Entwicklung, zahlt sich durch Risikominderung sowie Qualitäts- und Sicherheitsverbesserungen aus.

Dennoch bleibt das Testen eine Hürde für die Einführung von CI/CD-Workflows für embedded Systeme aufgrund der Automatisierung von Tests für diese Systeme. Der Schlüssel zur erfolgreichen Einführung von CI/CD ist, diese Hemmnisse durch die intelligente Testausführung und das Optimieren der Tests bei gleichzeitiger Verbesserung der Codeabdeckung zu überwinden.

Herausforderungen beim Testen von Embedded Software

Wegen der Notwendigkeit und Komplexität der Einführung und Beobachtung von Tests auf embedded Targets ist die Automatisierung von Tests für embedded Software eine Herausforderung. Dazu kommt in der Regel begrenzter Zugriff der Software-Teams auf die Zielhardware.

Aber Softwaretests zu automatisieren ist unerlässlich, damit man embedded Software durchgängig vom Host-Entwicklungssystem bis zum Zielsystem testen kann. Weil das Testen von embedded Software besonders zeitaufwändig ist, bringt die Automatisierung der Regressionstestsuite erhebliche Zeit- und Kosteneinsparungen.

Grundlagen

Zum Validieren und Einhalten von Standards ist es notwendig, Testergebnisse und Codabdeckungsdaten aus dem Zielsystem zu erfassen. Dies spielt bei der Testausführung eine entscheidende Rolle und kann durch Aufzeichnen und Pflege der Rückverfolgbarkeit zwischen Testfällen, Testergebnissen, Quellcode und Anforderungen erfolgen.

Hier setzen integrierte Testlösungen wie Parasoft C/C++test an und bieten ein optimiertes Testprogramm, das nur minimalen zusätzlichen Overhead für den binären Fußabdruck benötigt und in Form von Quellcode zur Verfügung gestellt wird, der sich anpassen lässt, wenn plattformspezifische Änderungen erforderlich sind.

Automatisieren von embedded Software Tests

Bild 1: Integrierte Testlösungen erlauben die leichte Integration von Testabläufen in den Workflow bei gleichzeitig geringem Aufwand und binärem Footprint.
Bild 1: Integrierte Testlösungen erlauben die leichte Integration von Testabläufen in den Workflow bei gleichzeitig geringem Aufwand und binärem Footprint.
(Bild: Parasoft)

Es gibt Lösungen, die spezielle Integrationen mit embedded IDEs und Debuggern bieten, um den Prozess der Ausführung von Testfällen reibungslos und automatisiert zu gestalten. So unterstützt die Testlösung von Parasoft für die Entwicklung von C/C++-Software das Erstellen von Regressionstest-Baselines als eine organisierte Sammlung von Tests und verifiziert automatisch alle Ergebnisse. Über die automatische Ausführung dieser Tests wird überprüft, ob Codeänderungen die in den Regressionstests erfasste Funktionalität verändern oder brechen.

Werden Änderungen eingeführt, schlagen diese Testfälle fehl, um das Team auf das Problem aufmerksam zu machen. Bei nachfolgenden Tests meldet Parasoft C++test Vorgänge, wenn es Änderungen an dem im ersten Test erfassten Verhalten feststellt.

Die Gleichheit der Fähigkeiten der Remote-Target-Ausführung mit Host-basierten Tests bedeutet, dass embedded Software-Teams von den gleichen Vorteilen der Automatisierung profitieren können wie bei jeder anderen Art der Anwendungsentwicklung auch.

Automatisierung ist wichtig, aber es reicht nicht, nur den Status quo zu erhalten. Um die Software-Sicherheit und -Qualität zu verbessern, braucht es mehr Tests innerhalb der CI/CD-Pipeline, ohne den Prozess weiter zu verlangsamen.

Testautomatisierung verbessern zur Optimierung von CI/CD

Die größte Schwierigkeit für die Teams besteht darin, die Testeffizienz zu verbessern und gleichzeitig die Anforderungen an Sicherheit und Qualität zu erfüllen, ohne die Zeitpläne und Kosten zu beeinträchtigen. Selbst bei einfacher Testautomatisierung (Testausführung und -ergebnisse) braucht es zwangsläufig Kompromisse, um die Testzeit angemessen zu halten.

Die einfache Lösung besteht darin, die zu testenden Teile der Software mit Hilfe von Vermutungen auszuwählen – schließlich sind vollständige Systemtestsuiten zu zeitaufwändig und teuer. Embedded-Softwareteams müssen ihre Tests ausweiten und sich gleichzeitig nur auf wirklich Erforderliches konzentrieren. Tests mit Hilfe intelligenter Testautomatisierung zu optimieren, macht das Rätselraten bei Testerstellung und -ausführung hinfällig.

Verbesserung von Tests mit Codeabdeckung

Generell ist die Codeabdeckung ein Maß dafür, wie viel vom Produktionscode ausgeführt wird, während die automatisierten Tests laufen. Das Ausführen einer Testreihe und Einsehen der Codeabdeckungsdaten liefern allgemeines Gefühl davon, wie viel von der Anwendung überprüft wird. Es gibt mehrere Arten der Codeabdeckung. Bei embedded Systemen muss man mit Anweisung, Verzweigung und MC/DC vertraut sein. Für die strengsten Anforderungen, wie z. B. bei sicherheitskritischer Software, kann eine Objektcodeverifizierung oder eine Assembler-Codeabdeckung notwendig sein.

Erfassen und Analysieren von Codeabdeckungsmetriken

Ein wichtiger Aspekt bei der Entwicklung von sicherheitskritischer embedded Software ist das Sammeln und Analysieren von Codeabdeckungsmetriken. Indem die Codeabdeckung die Vollständigkeit von Testfällen und ausgeführten Tests misst, liefert sie den Nachweis, dass die Validierung vollständig ist, zumindest gemäß Softwaredesign. Sie zeigt auch, dass kein unbeabsichtigtes Verhalten vorliegt - Code, der von keinem Test abgedeckt wird, ist eine Belastung, da sein Verhalten und seine Funktionalität unbekannt sind. Menge und Umfang der Codeabdeckung hängen von der Sicherheitsintegritätsstufe ab - je höher diese ist, desto strenger wird der Test durchgeführt und desto größer ist zwangsläufig die Anzahl und Komplexität der Testfälle.

Hier sind Beispiele für empfohlene Arten der Codeabdeckung:

  • Bei der Anweisungsabdeckung muss jede Programmanweisung mindestens einmal ausgeführt werden (die Verzweigungs- und MC/DC-Abdeckung umfasst die Anweisungsabdeckung).
  • Die Verzweigungsabdeckung stellt sicher, dass jeder mögliche Entscheidungszweig (if-then-else-Konstruktionen) ausgeführt wird.
  • Für die Modified Condition / Decision Coverage (MC/DC) ist eien möglichst vollständige Codeabdeckung notwendig, um sicherzustellen, dass die Testfälle jeden Entscheidungszweig und alle möglichen Kombinationen von Eingaben ausführen, die das Ergebnis der Entscheidungslogik beeinflussen. Bei komplexer Logik kann die Anzahl der Testfälle explodieren, so dass die modifizierten Bedingungseinschränkungen angewandt werden, um die Testfälle auf diejenigen zu begrenzen, die zu einer Änderung eigenständiger logischer Ausdrücke führen.

Moderne Tools zur Automatisierung von Unit-Tests liefern all diese Code-Abdeckungsmetriken und noch mehr. Beispielsweise automatisiert Parasoft C/C++test Lösung diese Datenerfassung bei Host- und Zieltests und erfasst den Verlauf der Testabdeckung über die Zeit. Diese Codeabdeckungshistorie kann Unit-, Integrations- und Systemtests beinhalten, um sicherzustellen, dass die Abdeckung auf allen Testebenen vollständig und nachvollziehbar ist.

Automatisiertes Erstellen von Unittest-Fällen

Schon immer war das Erstellen von produktiven Einheitstests eine Herausforderung, denn funktionale Sicherheitsstandards einzuhalten verlangt qualitativ hochwertige Software, was Testsuiten mit hoher Codeabdeckung voraussetzt. Teams benötigen Unit-Testfälle, mit denen sie ihre Abdeckungsziele erreichen können, die auch außerhalb des Bereichs der sicherheitskritischen Software wichtig sind. Jeder Code, den nicht mindestens ein Test abdeckt, wird ungetestet ausgeliefert!

Eine höhere Codeabdeckung kann sich als schwierig erweisen, denn das Prüfen von Verzweigungen im Code und die Suche nach Gründen, warum bestimmte Codeabschnitte nicht abgedeckt sind, ist immer wieder zeitraubend.

Abdeckungslücken beheben

Bild 2: Der Coverage Advisor in Parasoft C / C ++ - Test.
Bild 2: Der Coverage Advisor in Parasoft C / C ++ - Test.
(Bild: Parasoft)

Abdeckungslücken in Testsuiten behebt Parasoft C/C++test mit einem Coverage Advisor. Parasoft erkannte, wie man über die erweiterte statische Code-Analyse (Daten- und Kontrollflussanalyse) Werte für die Eingabeparameter finden kann, die für die Ausführung bestimmter Zeilen von nicht abgedecktem Code gebraucht werden. Diese Analyse berechnet die Vorbedingungen für Funktionsparameter, globale Variable und externe Funktionsaufrufe, die zur Ausführung einer bestimmten Codezeile notwendig sind. Der Coverage Advisor präsentiert eine Sammlung von Lösungen für die vom Benutzer ausgewählten Codezeilen, mit den Werten werden neue Unit-Testfälle erstellt. Diese Funktionalität steigert die Produktivität von Entwicklern, die an Unit-Testfällen arbeiten, um die Codeabdeckung zu verbessern.

Jede Abdeckungslösung umfasst:

  • Erforderliche Abhängigkeiten: Abhängigkeiten, die angepasst werden müssen, um die ausgewählte Zeile abzudecken. Dazu können Funktionsparameter, externe Funktionsaufrufe, globale Variablen, lokale Variablen und Klassenmitglieder gehören.
  • Vor-Bedingungen: Voraussetzungen, die von den erforderlichen Abhängigkeiten erfüllt werden müssen, um die ausgewählte Zeile abzudecken. Durch Anklicken einer Vorbedingung gelangt man zur entsprechenden Codezeile.
  • Erwartete Abdeckung: Codezeilen, die abgedeckt werden, wenn alle Vorbedingungen erfüllt sind.

Das Testen mit intelligenter Testausführung optimieren

Um das Testen in einer Continuous Pipeline zu beschleunigen, braucht es eine intelligente Testausführung auf Build-Basis, um die Anzahl der Tests zu reduzieren, die durchgeführt werden müssen, um das mit jeder neuen Iteration verbundene Risiko anzugehen. Die von der Testauswirkungsanalyse bereitgestellten Ergebnisse sind der Schlüssel dazu, dass sich das Testen nur darauf konzentriert, was unbedingt getestet werden muss anstelle des sonst üblichen Schrotflinten-Ansatzes.

Nur eine intelligente, datengestützte Entscheidungsfindung kann kontinuierliche Tests ermöglichen. Die Konzentration auf ein Minimum an Tests, um eine angemessene Abdeckung bei jeder Iteration zu gewährleisten, ist der Schlüssel, um die Agilität in agile Entwicklungsmethoden zurückzubringen.

Die intelligente Testausführung in Parasoft C/C++test wird durch Plugins für CI-Systeme (Jenkins, TeamCity, Bamboo usw.) erweitert und bietet moderne Funktionen, um die Softwareentwickler beim Reduzieren von Engpässen bei der Ausführung kontinuierlicher Builds zu unterstützen. Die gleichen Fähigkeiten stehen in IDE-Umgebungen mit speziellen Plugins zur Verfügung, die über eine REST-API auf ein zentralisiertes Abdeckungsbild zugreifen und bestimmen, welche Tests lokal in der IDE ausgeführt werden müssen, um den gesamten geänderten Code zu überprüfen.

Auf der Entwicklungsseite könnten erfahrene Entwickler ihre Tests richtig strukturieren und nur eine Teilmenge davon manuell ausführen, wobei sie vielleicht immer noch nicht wissen, welche Tests ausgeführt werden müssen, um ALLE Änderungen zu überprüfen.

Bild 3: Auswirkungen auf Tests durch Veränderungen.
Bild 3: Auswirkungen auf Tests durch Veränderungen.
(Bild: Parasoft)

Teams, die CI verwenden, können sich auf nächtliche Builds verlassen, um alle Tests automatisch über Nacht auszuführen und am nächsten Tag ein Feedback zu erhalten, aber nur, wenn es möglich ist, die gesamte Anzahl der Tests in weniger als 12 Stunden auszuführen.

Leider erledigen die meisten Softwareentwicklungsteams ihre täglichen Aufgaben mit diesen nicht skalierbaren Testverfahren. Noch schwieriger wird die Situation bei manuellen Tests. Konventionelle Softwareentwicklungsorganisationen folgen immer noch den Testpraktiken einer umgekehrten Pyramide, die aus dem einen oder anderen Grund das Durchführen manueller Tests über automatisierte Tests stellt.

Bild 4: Parasoft C/C++test erlaubt einen schnellen Überblick darüber, welche Dateien durch Codeveränderungen beenträchtigt wurden, und in welchem Maße.
Bild 4: Parasoft C/C++test erlaubt einen schnellen Überblick darüber, welche Dateien durch Codeveränderungen beenträchtigt wurden, und in welchem Maße.
(Bild: Parasoft)

Bei der intelligenten Testausführung wird die Testauswirkungsanalyse genutzt, um die Ausführung manueller Tests gegen Anwendungen und damit verbundene, erfasste Codeabdeckungsinformationen mit diesen Tests zu verfolgen. Eine ähnliche Technologie kommt bei automatisierten Tests zum Einsatz. Mit dieser Analyse wird ermittelt, welche manuellen Tests ausgeführt werden müssen, um auf geänderte Funktionen zuzugreifen, die mit jedem neuen Build geliefert werden. Daher kommt der intelligenten Testausführung auf der Ebene der Entwickler und Tester in ihren lokalen IDEs entscheidende Bedeutung zu. So können sie sich auf die Tests konzentrieren, die benötigt werden und müssen nicht mehr raten und zusätzliche Arbeit für den Fall der Fälle leisten.

* Ricardo Camacho ist Senior Technical Marketing Manager bei Parasoft.

(ID:47812540)