Testen von Embedded Software

Optimierte Unit-Tests für sicherheitskritische Systeme

10. Oktober 2022, 6:00 Uhr | Ricardo Camacho
Diesen Artikel anhören

Fortsetzung des Artikels von Teil 1

Wann ist das Testen abgeschlossen?

Eine scheinbar harmlose Frage – aber mit vielen verschiedenen Antworten. Bei vielen Projekten ist das Testen beendet, wenn keine Zeit mehr bleibt. Offiziell ist es jedoch beendet, wenn die Funktion der Anwendung nachgewiesen ist. Eine Antwort kann also lauten »wenn alle Anforderungen nachweislich erfüllt sind«. Natürlich ist diese Antwort bei manchen Anwendungen nicht ausreichend, was ist mit Leistungs-, Sicherheits- und anderen nicht-funktionalen Anforderungen?

Bei sicherheitskritischen Systemen muss nicht nur die Funktion, Sicherheit und Unbedenklichkeit nachgewiesen werden, sondern auch, dass es im Code kein Problem gibt, das von den Tests unberührt geblieben ist. Die Durchführung von Tests hängt von vielen Faktoren ab, aber in jedem Fall ist die Tool-Automatisierung der Schlüssel zur Erfassung der verschiedenen Metriken, die den Projektstatus anzeigen, egal ob es sich um Code- oder Anforderungsabdeckung handelt.

Die Analyse der Codeabdeckung kommt oft in Verbindung mit der Ausführung von Einheitstests zum Einsatz. Der Code ist instrumentiert, und während der Ausführung der Tests werden Daten über die ausgeführten Teile des Codes erfasst. Zudem können einige manuelle Funktionstests der Anwendung notwendig sein, um Code zu testen, der sich nur schwer mit Unit-Tests ausführen lässt, z.B. wenn Integrations- oder höhere Systemtests erforderlich sind. Um genaue und vollständige Informationen über die Codeabdeckung zu erhalten, müssen daher die Daten sowohl von Unit- als auch Funktionstests zusammengeführt werden, wobei Überschneidungen bei der Abdeckung zu berücksichtigen sind. Das ist sehr wichtig, wenn es sich bei dem System um eine sicherheitskritische Anwendung handelt, die eine präzise Abdeckung für die Produktzertifizierung oder -qualifizierung voraussetzt.

Mit einem Tool zur Codeabdeckung ist es viel einfacher, die Qualität der Testfälle zu analysieren. Die Erfahrung zeigt, dass selbst nach dem Ausführen aller Testszenarien, die alle Anforderungen abzudecken scheinen, einige Teile des Codes möglicherweise nicht ausgeführt wurden. Die Untersuchung der nicht abgedeckten Codeabschnitte ermöglicht es, zu entscheiden, ob es zusätzliche Tests braucht.

passend zum Thema

Metriken für die strukturierte Codeabdeckung.
Bild 4. Metriken für die strukturierte Codeabdeckung.
© Parasoft

Für gewöhnlich verlangen diese unentdeckten Codeabschnitte besondere Aufmerksamkeit - schließlich könnte es sich um Fehlerbehandlungscode oder Code zur Verarbeitung unerwarteter Eingaben handeln. Der einfachste Weg zum Check, ob ein solcher Code korrekt funktioniert, ist das Anwenden von Unit-Tests zusammen mit Stubbing-/Mocking-Frameworks, um das Standardverhalten von Softwarekomponenten außer Kraft zu setzen und die Codeausführung zu zwingen, seltene Pfade zu durchlaufen.

Farbcodierte Codeabdeckung mit prozentualen Kennzahlen.
Bild 5. Farbcodierte Codeabdeckung mit prozentualen Kennzahlen.
© Parasoft

Bild 4 zeigt verschiedene Abdeckungsmetriken, die zum Messen der Testabdeckung verwendet werden können, jeweils mit unterschiedlichem Schwierigkeitsgrad, der von den grundlegendsten Metriken (Zeile und Anweisung) bis zu umfassenderen Metriken wie der modifizierten Zustands-/Entscheidungsabdeckung (MC/DC) reicht. Welche davon zu verwenden ist, wird in der Regel durch das mit dem zu testenden Code verbundene Risiko bestimmt. Beispielsweise definiert in der ISO-Norm 26262 der Automotive Safety Integrity Level (ASIL), welche Abdeckungsarten genutzt werden sollten: Level A kann nur »Statement« verwenden, Level D dagegen verlangt »Branch« und »MC/DC«.

Intelligente UI-Benachrichtigungen.
Bild 6. Intelligente UI-Benachrichtigungen.
© Parasoft

Ein Beispiel für einen Code-Abdeckungsbericht direkt in der IDE von C/C++test zeigt Bild 5, mit den während der Testausführung abgedeckten und nicht abgedeckten Zeilen.

Das Beispiel in Bild 6 zeigt eine bestimmte Bedingung, die nicht im Rahmen von MC/DC abgedeckt ist.

Finde den einen durchgerutschten Fehler

Trotz erheblicher Investitionen in Unit-Tests wird es weiterhin Fehler geben, insbesondere Sicherheitslücken, die es durch die Unit-Tests und in spätere Entwicklungsstadien schaffen. Im schlimmsten Fall gelangen sie in den Produktionscode, wo sie teuer zu beheben sind und weitere Kosten verursachen, z.B. für eine erneute Zertifizierung oder tatsächliche Sicherheitsvorfälle. Mit der statischen Analyse und der Laufzeitanalyse gibt es zwei wichtige Techniken mit niedrigen Kosten und geringem Implementierungsaufwand. Bei der statischen Analyse wird der Quellcode auf Abweichungen von Best Practices untersucht, die zu Fehlern, Sicherheitslücken und Zuverlässigkeitsproblemen in Produktionsumgebungen führen können. Die Laufzeitanalyse hingegen ist die Instrumentierung und Überwachung der Laufzeitausführung von Anwendungen, um »echte« Fehler zur Laufzeit zu entdecken.

Statische Analyse

Die statische Analyse in C/C++test verifiziert die Qualität von C- und C++-Code und prüft das Einhalten von Sicherheits-, Funktionssicherheits- und Industriestandards – z.B. CERT, MISRA C oder JSF AV C++ – durch den Einsatz eines breiten Spektrums von Checkern und eines umfassenden Satzes an Techniken wie musterbasierte Analyse, Daten-/ Kontrollflussanalyse und Software-Metriken. Sie erfüllt folgende Funktionen; 

  • Analyse der Konformität von Code mit Industriestandards wie MISRA C 2012, MISRA C++ 2008, JSF AV C++, SEI CERT C/C++, AUTOSAR C++14, HIC++ und anderen.
  • Erkennen von Laufzeitproblemen ohne Ausführen von Code. Mithilfe der Datenflussanalyse zur Untersuchung der Ausführungspfade durch den Code lassen sich Probleme wie Nullzeiger-Dereferenzierung, Pufferüberläufe, Division durch Null und Speicherlecks aufdecken.
  • Erstellen von Checkern für benutzerdefinierte Programmierstandards. Die statische Analyse kann unternehmens- oder teamspezifische Richtlinien und Programmierungsstandards überprüfen, beispielsweise enthält C/C++test einen Editor für das Erstellen von benutzerdefinierten Regeln, die über die integrierten Regeln hinausreichen können.

Laufzeitanalyse

Die Laufzeitanalyse findet Laufzeitdefekte, Stabilitätsprobleme und Sicherheitsschwachstellen wie Speicherlecks, Nullzeiger, nicht initialisierter Speicher und Pufferüberläufe, indem sie die ausgeführte Anwendung sowohl während der Ausführung von Unit-Tests als auch während der manuellen Ausführung der Anwendung überwacht. Sie beinhaltet:

  • Erkennen von Laufzeitproblemen während der Ausführung einer Anwendung.
  • Erkennen von Laufzeitproblemen beim Ausführen von Unit-Tests. Die Laufzeitanalyse kann eine Test-Binärdatei bei der Ausführung von Unit-Tests überwachen und so zusätzliche Einblicke in den zu testenden Code geben, um Fehler oder Instabilitäten bei Unit-Tests besser zu verstehen.
  • Durchführen von Laufzeitprüfungen für plattformübergreifende Umgebungen. Die Laufzeitanalyse kann nicht nur für native Anwendungen, sondern auch für plattformübergreifende und Embedded-Umgebungen ausgeführt werden, so dass die Analyse in der ursprünglichen »Produktions«-Umgebung erfolgt, die in vielen Fällen nur begrenzte Test- und Debugging-Möglichkeiten bietet.
  • Kombination der Ergebnisse der Laufzeitanalyse mit anderen Testdaten. Laufzeitdefekte werden in der IDE auf einheitliche Weise mit anderen Testdaten wie Codeabdeckung, Fehlern bei Unit-Tests oder Ergebnissen der statischen Analyse dargestellt. Das erleichtert Prüfung und Verstehen der Grundursache eines Laufzeitdefekts.

CI/CD für DevOps

Moderne DevOps- und Continuous-Everything-Initiativen setzen die Fähigkeit zur sofortigen und kontinuierlichen Bewertung der mit einem Release-Kandidaten verbundenen Geschäftsrisiken voraus. Kontinuierliches Testen hilft den Entwicklungsteams, die geschäftlichen Erwartungen zu erfüllen, und den Managern bei fundierten Kompromissentscheidungen, um den unternehmerischen Wert eines Release-Kandidaten zu optimieren. Einen Großteil dieser Aufgabe erfüllt die kontinuierliche Integration (CI).

Um moderne agile Methoden wie DevOps zu unterstützen, bietet es sich an, dass Teams Testmethoden wie Unit Testing, statische Analyse und strukturelle Codeabdeckung in ihr CI/CD-System integrieren. Dadurch können sie auf die automatisierten Testergebnisse in der IDE, mit HTML/XML-Berichten oder zusammengefasst in einem zentralen Dashboard zugreifen, z.B. im Tool DTP von Parasoft, für weitere Nachbearbeitungen, Berichte und Auswertungen.

Wie bei Unit-Tests können Entwickler statische Analysen in der IDE oder über die Befehlszeilenschnittstelle für Automatisierungs- und kontinuierliche Integrationsszenarien ausführen. Sie erhalten sofortigen Zugriff auf die Analyse-Ergebnisse in der IDE, mit HTML/XML-Berichten oder aggregiert in einem zentralen Dashboard in DTP für die weitere Nachbearbeitung, Reporting und Analyse.


  1. Optimierte Unit-Tests für sicherheitskritische Systeme
  2. Wann ist das Testen abgeschlossen?
  3. Alles bündeln: Innovative Berichterstattung und Analytik

Lesen Sie mehr zum Thema


Jetzt kostenfreie Newsletter bestellen!

Weitere Artikel zu Parasoft

Weitere Artikel zu Parasoft Corp.