Empfohlenes Webinar: MISRA C++ 2023: Alles, was Sie wissen müssen | Zum Video

Messung der Codeabdeckung: Leitfaden für effektives Testen

Kopfbild von Ricardo Camacho, Director of Safety & Security Compliance
20. November 2023
10 min lesen

Die Codeabdeckung hängt stark von der Genauigkeit ab. Es beinhaltet die Auswahl nur der für Ihr Projekt erforderlichen Deckung. Die beiden wichtigsten Fallstricke bei der Codeabdeckung werden in diesem Artikel ausführlich erörtert, zusammen mit der Frage, wie man sie verhindert.

Was ist Code-Abdeckung?

Die Essenz von Codeabdeckung stellt Code offen, der aufgrund der Durchführung Ihrer Softwaretests nicht ausgeführt wurde. Aufgedeckter Code macht deutlich, wo möglicherweise Fehler in der Anwendung lauern und dass Ihnen Testfälle zur Behebung dieser aufgedeckten Bereiche fehlen.

Der gebräuchlichste Ansatz zur Überwachung des Codes während seiner Ausführung und zum Erreichen der Codeabdeckung ist die Instrumentierung des Codes. Dies bedeutet, dass der vorhandene Code mit zusätzlichem Code ergänzt wird und weiter angepasst werden kann, um zu erkennen, ob Codierungsstrukturen wie eine Anweisung, Funktion, Bedingung, Entscheidung, Verzweigung und andere ausgeführt wurden. Dies ist wichtig, da verschiedene logische Ausführungspfade eingeschlagen werden können. Sie möchten daher sicherstellen, dass Sie diese angewendet und unsicheres, unsicheres oder unvorhersehbares Verhalten aufgedeckt haben.

Vorteile der Messung der Codeabdeckung

Sie möchten keine Deckung um der Deckung willen. Sie benötigen eine aussagekräftige Berichterstattung, die zeigt, dass Sie beim Testen der Software gute Arbeit geleistet haben. Die Codeabdeckung deckt nicht nur Lücken in Ihren Tests auf, sondern deckt auch toten Code auf.

Toter Code ist Code, der in der Anwendung vorhanden ist, aber es gibt kein mögliches Ausführungsszenario, in dem dieser Code jemals ausgeführt werden könnte. Manchmal ändern sich unsere Anforderungen, also ändern wir die Logik. In komplexen Softwaresystemen erkennen wir nicht jedes Ergebnis und jede Auswirkung, wie zum Beispiel, dass es jetzt eine Funktion oder Codestruktur gibt, die niemals ausgeführt wird. Daher kann toter Code auf einen Fehler in der Logik hinweisen, es handelt sich jedoch zumindest um ein Sicherheitsrisiko, das behoben werden muss.

Die Messung der Codeabdeckung bietet auch Vorteile für den Endbenutzer oder Stakeholder. Wenn die Anwendung getestet wurde und 100 % des Codes abgedeckt sind, vermittelt das das warme und wohlige Gefühl, etwas von Qualität zu liefern, im Vergleich zu dem Gefühl, eine Anwendung zu liefern, bei der nur 60 % Codeabdeckung erreicht wurden. Dies ist auch der Grund, warum Branchenstandards zur funktionalen Sicherheit wie DO-178C, ISO 26262, IEC 62304, IEC 61508, EN 50128 und viele andere vorschreiben oder dringend empfehlen, dass Entwicklungsteams eine Codeabdeckung durchführen.

Code-Coverage-Metriken

Funktionale Standards bestimmen auch die Art der zu erreichenden Abdeckungsmetriken und einige der verschiedenen zu verwendenden Testmethoden. Wie bereits erwähnt, gibt es Codierungsstrukturen wie Anweisungen, Verzweigungen, Entscheidungen usw., die nur durch ganz bestimmte Bedingungen ausgeübt werden. Diese Bedingungen hängen von bestimmten Variablen und deren Werten zum richtigen Zeitpunkt ab.

Basierend auf den richtigen Bedingungen können die verschiedenen Ausführungspfade verfolgt und Metriken gesammelt werden. Daher müssen Sie mehrere Testfälle erstellen, um die Anwendung mit den richtigen Daten zu versorgen und die gewünschte Bedingung zu schaffen.

Um Codeabdeckungsmetriken zu sammeln, können Teams verschiedene Testabdeckungsmethoden wie Unit-Tests, Systemtests, manuelle Tests und mehr verwenden.

Testabdeckung

Da Qualitätssicherungsteams (QA) Systemtests durchführen müssen, nutzen viele Organisationen ihre Systemtestfälle, um Codeabdeckung zu erhalten. Es kommt jedoch häufig vor, dass Tests auf Systemebene nicht die erforderlichen Abdeckungsziele liefern. In der Regel wird eine Abdeckung von 60 % erreicht, was viel Raum für unentdeckte Probleme lässt. Daher kann es sein, dass Teams am Ende die Abdeckung aus Unit-Tests, Integrationstests und manuellen Tests zusammenfassen.

Zusammen können Sie mit diesen Testmethoden eine 100-prozentige Codeabdeckung oder das gewünschte Ziel erreichen. Aber Unternehmen müssen auch den Grad der erforderlichen strukturellen Codeabdeckung verstehen. Funktionale Sicherheitsstandards schreiben vor oder empfehlen, dass die Codeabdeckung aus einer Anweisungs-, Verzweigungs- und/oder modifizierten Bedingungsentscheidungsabdeckung (MC/DC) besteht. Dies wird durch den für Ihre Anwendung festgelegten Sicherheitsintegritätslevel (SIL) bestimmt.

Je höher das Risiko für Personen und Sachwerte bei einem Ausfall der Software ist, desto mehr strukturelle Codeabdeckungen sind erforderlich. Die strengsten Anforderungen an die Codeabdeckung bestehen in Anwendungen des Avionikstandards DO-178C Level A, bei denen die Codeabdeckung auf Assembler-Codeebene zusätzlich zu Anweisung, Verzweigung und MC/DC gilt. Zum Glück für unsere Kunden automatisiert Parasoft die Assembler-Code-Abdeckung – auch bekannt als Objektcode-Verifizierung – als Teil unseres Lösungsangebots.

Aussagedeckung

Die Anweisungsabdeckung antwortet, wenn jede Anweisung in der Softwareanwendung ausgeführt wurde. Eine Anweisung ist eine einzelne syntaktische Einheit der Programmiersprache, die eine auszuführende Aktion ausdrückt.

Hier ist ein einfaches Anweisungsbeispiel: int* ptr = ptr + 5;

Zustandsabdeckung

Die Bedingungsabdeckung beantwortet die Frage: Wurde jeder boolesche Unterausdruck sowohl als wahr als auch als falsch ausgewertet? Bedingungen werden basierend auf Beziehungsoperatoren wie ==, !=, <, > und anderen als wahr oder falsch ausgewertet. Basierend auf dem bewerteten Ergebnis werden unterschiedliche Ausführungspfade durchgeführt. Für die Bedingungsabdeckung einer Bedingung wie (A > 7) benötigen Sie also zwei Testfälle. Ein Testfall, bei dem A gleich 0 ist, was ein wahres Ergebnis erfüllt, und ein Testfall, bei dem A gleich 9 ist, was ein falsches Ergebnis erfüllt.

Entscheidungsabdeckung

Die Entscheidungsabdeckung beantwortet die Frage: Wurde jeder nicht-boolesche Unterausdruck sowohl als wahr als auch als falsch ausgewertet? Entscheidungen sind Ausdrücke, die aus Bedingungen und einem oder mehreren der logischen Operatoren && oder || bestehen. Um eine Entscheidungsabdeckung für eine Entscheidung wie ((A>7) && (B<=0)) zu erreichen, benötigen Sie Testfälle, die für jede Entscheidung ein wahres und ein falsches Ergebnis zeigen.

  • Ein Testfall, bei dem A größer als 7 und B kleiner oder gleich 0 ist, erfüllt ein wahres Ergebnis.
  • Ein Testfall, bei dem A kleiner als 7 oder B größer als 0 ist, erfüllt ein falsches Ergebnis.

Es ist wichtig zu beachten, dass der Entscheidungsabdeckungsbegriff überladen ist. Für einige Branchen bedeutet Entscheidungsabdeckung auch Branchenabdeckung.

Zweigstellenabdeckung

Die Zweigabdeckung beantwortet die Frage: Wurde jeder „Pfad“ in einer Bedingungs- und Entscheidungskontrollstruktur (if, switch, while Catch usw.) ausgeführt? In einigen komplizierten Codekonstrukten reicht die Zweigabdeckung nicht aus, daher wird zusätzlich zur Zweigabdeckung eine Entscheidungsabdeckung mit modifizierter Bedingung empfohlen.

Modifizierte Bedingungsentscheidungsabdeckung (MC/DC)

MC/DC beantwortet die Frage: Wurden alle Bedingungen in Entscheidungen mindestens einmal im Hinblick auf alle möglichen Ergebnisse bewertet? Es ist eine Kombination aus Zweig-, Bedingungs- und Entscheidungsabdeckung, aber viel stärker. Es stellt sicher, dass:

  • Jede Kontrollanweisung erfordert mindestens einmal alle möglichen Ergebnisse.
  • Jede Bedingung erfordert mindestens einmal alle möglichen Ergebnisse.
  • Es hat sich gezeigt, dass jede Bedingung in einer Entscheidung unabhängig voneinander das Ergebnis dieser Entscheidung beeinflusst.

Tabelle mit Testfällen MC/DC 100 % Abdeckung mit Bedingungen und Ergebnissen.

Wenn Sie die in der Tabelle gezeigte Bedingungsentscheidungsanweisung (C1 || (C2 && C3)) verwenden, hat sie:

  • Drei Bedingungen: C1, C2 und C3
  • Zwei Entscheidungen: das ODER (||) und das UND (&&)

Basierend auf den MC/DC-Anforderungen gilt: Wenn Bedingung 1 falsch ist und die Entscheidung ein ODER ist, müssen Sie Bedingung 2 und 3 bewerten, um die Auswirkung auf das Ergebnis zu bestimmen.

Wenn jedoch Bedingung 1 wahr ist, ist das Ergebnis automatisch wahr und Sie müssen Bedingung 2 und 3 nicht bewerten.

Für MC/DC gibt es eine Formel zur Bestimmung der Mindestanzahl an Testfällen, die erforderlich sind, um eine 100 %ige MC/DC-Abdeckung zu erreichen. Es handelt sich um die Anzahl der Bedingungen plus 1. Die Tabelle verdeutlicht dies deutlich.

Andere Deckungsarten

Weitere Deckungsarten beantworten die folgenden Fragen.

  • Funktionsabdeckung. Wurde jede Anweisung einer Funktion in der Anwendung ausgeführt?
  • Anrufabdeckung. Wurde jede Funktion im Programm aufgerufen?
  • Leitungsabdeckung. Wurde jede Zeile im Programm ausgeführt?
  • Kantenabdeckung. Wurde jeder Zweig im Programm ausgeführt?
  • Pfadabdeckung. Wurde jede mögliche Route durch einen bestimmten Teil des Codes ausgeführt?
  • Ein-/Ausreiseschutz. Wurden alle möglichen Aufrufe und Rückgaben der Funktion ausgeführt?
  • Schleifenabdeckung. Wurde jede mögliche Schleife nullmal, einmal oder mehr als einmal ausgeführt?
  • Abdeckung blockieren. Wurde jede Gruppe von Anweisungen in der Begin-End- oder If-Else-, Case-, Wait-, While- oder For-Schleife usw. ausgeführt?

Wie misst man die Codeabdeckung?

Da es verschiedene Strukturtypen der Codeabdeckung gibt, gibt es für jeden Codeabdeckungsmetriken. Wenn Ihr Ziel oder Ihre Anforderung eine 100 %ige Kontoauszugs-, Zweigstellen- und MC/DC-Abdeckung ist, müssen Sie eine 100 %ige Kontoauszugs-, 100 %-Zweigstellen- und 100 % MC/DC-Abdeckung erfüllen.

Es gibt auch Codierungskonstrukte, bei denen keine Testfälle für eine bestimmte Codezeile erstellt werden können. Beispielsweise folgt eine Return-Anweisung einer Endlosschleife. Bei sicherheitskritischen Anwendungen, bei denen eine 100-prozentige Anweisungsabdeckung vorgeschrieben ist, können Benutzer diese Codezeile messen und berücksichtigen, indem sie sie in einem Debugger schrittweise durchgehen. Diese visuelle Inspektion ist als Ansatz zur Messung der Codeabdeckung akzeptabel und gültig. Um den Aufwand für die Erfassung der Codeabdeckung zu erleichtern, ist es wichtig, die beste verfügbare Lösung auszuwählen. Parasoft ist diese Lösung.

Screenshot des Parasoft C/C++-Tests mit ausgewählter Anweisungsabdeckung als Typ, der sich auf die Ausführung von Komponententests vorbereitet.

Schritt 1: Wählen Sie ein Code Coverage Tool

Wie bereits erwähnt, wird die Codeabdeckung durch den Einsatz verschiedener Testmethoden wie manuelle Tests, Unit-Tests, Systemtests und mehr erfasst. Außerdem ist Code instrumentiert, um die Codeausführung zu erkennen und verschiedene strukturelle Codeabdeckungstypen wie Anweisung, Verzweigung und MC/DC zu erfassen.

Zusätzlich für sicherheitskritische SystemeEinige Stakeholder verlangen eine Codeabdeckung auf der tatsächlichen Zielhardware und eine Zertifizierung des Abdeckungstools für den Einsatz auf sicherheitskritischen Systemen. Daher ist die Auswahl eines Code-Coverage-Tools ein äußerst wichtiger Schritt, da er den Weg für eine reibungslose und produktive Reise ebnet.

Schritt 2. Integrieren Sie das Tool

Code-Coverage-Lösungen von Parasoft lassen sich nahtlos in IDEs wie Eclipse, MSVS, VS Code und viele andere Editoren integrieren und ermöglichen so eine intuitive Verwendung und Bereitstellung. Organisationen können sich auch dafür entscheiden, ihre Anwendung zu instrumentieren und ihre vorhandenen Tests auszuführen. Darüber hinaus wird ein Abdeckungsbericht erstellt.

Die meisten Kunden integrieren die Code-Coverage-Lösung von Parasoft in ihre Continuous Integration (CI)-Pipeline. Im Rahmen des Build-Prozesses wird der Code instrumentiert. Während der DevOps-Testphase wird die Codeabdeckung erfasst. Bei sicherheitskritischen Anwendungen kann die Codeabdeckung auch während der Codeausführung auf der Zielhardware erfasst werden.

Schritt 3. Tests schreiben und ausführen

Parasoft-Entwicklungs- und Testtools wie C / C ++ - Test und Test Automatisieren Sie die Testfallerstellung für Unit-Tests, Integrationstests und Systemtests. Funktionen wie die automatisierte Erstellung von Unit-Testfällen können durch die Ausführung der automatisch erstellten Testfälle eine Codeabdeckung von bis zu 80 % oder mehr erzielen.

Die automatisch generierten Testfälle sind auch intelligente Testfälle, was bedeutet, dass der Code analysiert und Testfälle erstellt werden, um echte Fehler aufzudecken, wie z. B. außerhalb der Grenzen liegende Bedingungen, Nullzeiger, Pufferüberläufe, Division durch Null und mehr. Diese Lösung auf Knopfdruck ermöglicht enorme Arbeitseinsparungen und eine unglaubliche Produktivitätssteigerung. GUI-Editoren und die Assistentenfunktion mit Schritt-für-Schritt-Anleitung erleichtern die Erstellung von Testfällen.

Schritt 4: Abdeckungsbericht erstellen

Die Berichterstattung über die Parasoft-Codeabdeckung ist außergewöhnlich. Mit hervorgehobenen, farbcodierten Zeilen, die in Ihrer bevorzugten IDE oder Ihrem bevorzugten Code-Editor sichtbar sind, generiert DTP Codedateien in Farbe, um Codezeilen visuell zu erkennen, die nicht getestet und für Prüfzwecke verfügbar gemacht wurden. Am überzeugendsten ist Die DTP-Web-Dashboard-Analyse- und Berichtslösung von Parasoft. Es zeigt laufende Code-Coverage-Diagramme, Risikobereiche und gezielte Widgets zur Statement-Coverage, Branch-Coverage und mehr an. Dies sind genau die umfassenden Daten, die das Management benötigt, um den Fortschritt über die Codeabdeckung hinaus zu überwachen. Es zeigt auch den Zustand des Projekts und die Codequalität rundum an.

Schritt 5. Überprüfen und verbessern

Da unsere Code-Coverage- und Software-Testlösungen speziell für die Integration in Ihren CI/CD-Workflow konzipiert sind, können Teams bei jedem Sprint-Review-Meeting ihren Fortschritt überprüfen, sich an veränderte Anforderungen anpassen und Prozesse verbessern, die die Produktivität und Codequalität steigern. Parasoft-Lösungen sind so konzipiert, dass sie moderne agile Methoden unterstützen. Aus diesem Grund lässt sich Parasoft in GitHub, GitLab, Azure DevOps, Bazel, Jira, Jenkins, Bamboo und mehr integrieren.

Die zwei großen Fallen der Code-Abdeckung

Die Messung der Codeabdeckung ist eines der Dinge, die meine Aufmerksamkeit immer erregen. Einerseits stelle ich oft fest, dass Organisationen nicht unbedingt wissen, wie viel Code sie beim Testen abdecken, was überraschend ist! Am anderen Ende des Abdeckungsspektrums gibt es Organisationen, für die die Zahl so wichtig ist, dass die Qualität und Wirksamkeit der Tests weitgehend irrelevant geworden sind.

Die Codeabdeckung kann eine gute und interessante Zahl zur Beurteilung der Qualität Ihrer Software sein, aber es ist wichtig zu bedenken, dass sie eher ein Mittel als ein Zweck ist. Wir wollen keine Berichterstattung um der Berichterstattung willen. Wir wünschen eine Berichterstattung, weil sie zeigen soll, dass wir beim Testen der Software gute Arbeit geleistet haben. Wenn die Tests selbst nicht aussagekräftig sind, bedeutet eine größere Anzahl davon sicherlich nicht, dass die Software besser ist. Das wichtige Ziel besteht darin, sicherzustellen, dass jeder Codeabschnitt getestet und nicht nur ausgeführt wird.

Dies bedeutet, dass eine geringe Abdeckung zwar bedeutet, dass wir wahrscheinlich nicht ausreichend testen, eine hohe Abdeckung allein jedoch nicht unbedingt mit einer hohen Qualität korreliert. Das Bild ist komplizierter.

Falle Nr. 1: „Wir kennen unsere Abdeckung nicht“

Es erscheint mir unvernünftig, Ihren Versicherungsschutz nicht zu kennen. Deckungstools sind günstig und reichlich vorhanden. Ein echtes Problem, auf das Teams bei der Messung der Abdeckung stoßen, ist, dass das System zu kompliziert ist. Wenn Sie eine Anwendung aus mehreren Teilen erstellen, kann es eine entmutigende Aufgabe sein, allein zu wissen, wo die Abdeckungszähler platziert werden sollen. Ich würde vorschlagen, dass Sie zweimal über die Architektur nachdenken sollten, wenn es schwierig ist, die Abdeckung in Ihrer Anwendung zu messen.

Eine zweite Möglichkeit, in diese Falle zu tappen, besteht bei Organisationen, die zwar viele Tests durchführen, aber keine wirkliche Abdeckungszahl haben, weil sie nicht über ein Tool oder eine Lösung zur Codeabdeckung verfügen, die die Zahlen aus verschiedenen Testläufen aggregieren kann. Wenn Sie manuelle Tests, Funktionstests, Unit-Tests und andere Arten durchführen, stellen Sie sicher, dass das von Ihnen verwendete Tool die Abdeckung für alle Ihre Testmethoden ordnungsgemäß aggregieren kann.

Bei Parasoft nutzen wir die große Menge detaillierter Daten, die mit dem Berichts- und Analysetool Parasoft DTP erfasst werden, das eine umfassende, aggregierte Ansicht der Codeabdeckung im Kontext bietet. Anwendungsmonitore sammeln Abdeckungsdaten direkt von der Anwendung, während diese getestet wird, und senden diese Informationen dann an DTP, das Abdeckungsdaten über alle Testpraktiken, Testteams und Testläufe hinweg aggregiert.

Wenn das nach einer ziemlich großen Menge an Informationen klingt, haben Sie Recht! DTP bietet ein interaktives Dashboard, das Ihnen hilft, durch die Daten zu navigieren und Entscheidungen darüber zu treffen, worauf Sie Ihre Testbemühungen konzentrieren sollten. Sehen Sie sich das Beispiel-Dashboard unten an.

Screenshot des Parasoft DTP Report Center-Dashboards mit Analyse der Anwendungsabdeckung.

Wenn mehrere Tests denselben Code abgedeckt haben, wird er nicht überzählt. Ungetestete Teile des Codes sind schnell und einfach zu erkennen. Hier sehen Sie, welche Teile der Anwendung gut getestet wurden und welche nicht.

Also keine Ausreden mehr, die Abdeckung nicht zu messen.

Falle Nr. 2: „Abdeckung ist alles!“

Es kommt häufig vor, dass man fälschlicherweise denkt, dass Deckung alles ist. Sobald Teams die Abdeckung messen können, ist es nicht ungewöhnlich, dass Manager sagen: „Lasst uns diese Zahl erhöhen.“ Letztendlich wird die Zahl selbst wichtiger als der Test. Die vielleicht beste Analogie stammt von Adam Kolawa, dem Gründer von Parasoft:

„Es ist, als würde man einen Pianisten bitten, 100% der Klaviertasten abzudecken, anstatt nur die Tasten zu drücken, die im Kontext eines bestimmten Musikstücks sinnvoll sind. Wenn er das Stück spielt, bekommt er jede Menge Key-Coverage, die Sinn macht. “

Darin liegt das Problem. Gedankenlose Berichterstattung ist dasselbe wie sinnlose Musik. Die Abdeckung muss die tatsächliche, sinnvolle Nutzung des Codes widerspiegeln.

In bestimmten Branchen, beispielsweise in sicherheitskritischen Branchen, ist eine 100-prozentige Abdeckung erforderlich. Aber selbst in diesem Fall ist es allzu einfach, die Ausführung einer Codezeile als sinnvollen Test zu betrachten, was möglicherweise nicht stimmt. Um festzustellen, ob ein Test ein guter Test ist, stellen Sie die folgenden zwei grundlegenden Fragen.

  1. Was bedeutet es, wenn der Test fehlschlägt?
  2. Was bedeutet es, wenn der Test bestanden wird?

Wenn Sie eine dieser Fragen nicht beantworten können, liegt wahrscheinlich ein Problem mit Ihrem Test vor. Wenn Sie keine dieser Fragen beantworten können, ist der Test wahrscheinlich mehr Aufwand als er wert ist. Der Ausweg aus dieser Falle besteht zunächst darin, zu verstehen, dass das eigentliche Ziel darin besteht, nützliche und aussagekräftige Tests zu erstellen. Abdeckung ist wichtig. Die Verbesserung der Abdeckung ist ein erstrebenswertes Ziel.

Abschließende Erkenntnisse: Code-Abdeckung als Ihr Weg zu Test-Exzellenz

Während die Codeabdeckung eine wertvolle Metrik ist, die dabei helfen kann, ungetestete Teile des Codes zu identifizieren und die Gesamtqualität einer Codebasis zu verbessern, sollte sie als ein Aspekt einer umfassenderen Teststrategie betrachtet werden. Selbst das Erreichen einer 100-prozentigen Codeabdeckung bedeutet nicht unbedingt, dass keine Fehler vorliegen oder die Korrektheit der Software gewährleistet ist. Dies allein ist keine Garantie für hervorragende Testergebnisse.

Es ist eine von mehreren Praktiken, die zu einem umfassenden Testansatz beitragen. Die Codeabdeckung kann Teil der Qualitätssicherungsbemühungen sein, aber auch andere Aspekte des Testens, wie Unit-Tests, Integrationstests, Systemtests und Benutzerakzeptanztests, sind ebenso wichtig. Testexzellenz umfasst eine Kombination aus verschiedenen Testarten, gutem Testdesign, aussagekräftigen Testfällen sowie einer kontinuierlichen Bewertung und Verbesserung des Testprozesses.

Umfassende Codeabdeckung: Gesamtabdeckung über Testpraktiken hinweg