Überdeckungsmetriken zur Bestimmung der Testgüte (Whitebox-Testing)

tl/dr; (ca. 15min Lesezeit): Wie werden die einzelnen Whitebox-Testmetriken berechnet? Welche Vorteile und Nachteile bieten Anweisungsüberdeckung, Zweigüberdeckung, Pfadüberdeckung, Bedingungsüberdeckung? Welche praktische Relevanz haben diese Metriken? Es gibt Beispiele der theoretischen Grundlagen mit Javaquelltext und Kontrollflussgraphen. Die praktische Arbeit mit einem CodeCoverage-Tool wir in einem andern Blog-Artikel beleuchtet.

Wird ein vorhandenes Programm getestet, so gibt die Tatsache, ob ein Test scheitert oder bestanden wird, für sich genommen noch kein Maß über die Qualität einer Software an. Vielmehr ist es die Güte des Tests selbst, die entscheidet, wie aussagekräftig dessen Ergebnis ist.

Um die Güte der Testfälle selbst messbar zu machen (und somit deren Qualität zu quantifizieren), kann ein Maß bestimmt werden, dass angibt, wie viele Aspekte des Programmcodes durch die Tests erfasst werden.

Grundlegende Berechnung der Überdeckungsgrade

Die Güte eines White-Box-Tests wird mit Hilfe eines Überdeckungsgrads C ausgedrückt, der angibt, wie viel Prozent der theoretisch möglichen Aspekte durch den Test erfasst werden.

C = \frac{\sum{Anzahl\:getesteter\:Aspekte}}{\sum{Anzahl\:aller\: Aspekte}}

Dieser Überdeckungsgrad kann für unterschiedliche Aspekte berechnet werden:

  • Wie viele Prozent der Codezeilen durchlaufen meine Testfälle?

  • Wie viele Prozent der Anweisungen durchlaufen meine Testfälle?

  • Wie viele Prozent der Programmzweige durchlaufen meine Testfälle?

  • Wie viele Prozent der Programmpfade durchlaufen meine Testfälle?

  • Wie viele Prozent der (Teil-)Bedingungen werden von meinen Testfälle jeweils erfüllt und verletzt?

Im Idealfall erhält man für einzelne Aspekte mit den vorhandenen Testfällen eine Überdeckung von 100% - beispielsweise führen die Testfälle alle vorhandenen Anweisungen einmal aus.

Ein hoher Überdeckungsgrad allein sagt noch nichts über die Fehlerfreiheit des Codes aus. Es werden so jedoch Bereiche aufgedeckt, die bisher noch nicht getestet oder gar nicht erreichbar waren. Die Ergänzung von Testfällen über eine Überdeckungsmethodik sollte daher nur als Ergänzung zur Äquivalenzklassenbildung und Grenzwertanalyse der Blackbox-Tests gesehen werden und stellt für sich genommen keine hinreichende Testart dar.

An einem Codebeispiel

Die einzelnen Überdeckungsgrade sollen an eine Methode zur Berechnung von Rabatten dargestellt werden.

Es werden Rabatte errechnet in Abhängigkeit der Positionskosten (über 100 Euro -> 5 Euro Rabatt) sowie der Gesamtkosten (über 1000 Euro -> 5% Rabatt auf die rabattierte Gesamtsumme):

Zum Verständnis der Überdeckung ist es oft hilfreich, statt des Quelltextes den Kontrollflussgraphen zu betrachten. Im Kontrollflussgraphen werden alle Anweisungen/Operationen als Knoten (nodes) erfasst, alle Übergänge und Verzweigungen als Kanten (edges).

Für unser Ausgangsbeispiel sieht ein noch nicht vereinfachter Kontrollflussgraph z.B. so aus:

Kontrollflussgraph der Rabattfunktion
Kontrollflussgraph der Rabattfunktion

Der Graph umfasst neben dem Knoten und der Kante zur Kennzeichnung von Start/Stop insgesamt:

  • 10 Anweisungen (mit Zahlen 1-10 gekennzeichnet)

  • 12 Kanten (mit Kleinbuchstaben a-l gekennzeichnet)

Zur Übersicht sind hier die einzelnen Operationen und Verzweigungsbedingungen noch genannt - in Kontrollflussgraphen kann dies aber weggelassen werden. Darüber hinaus können Programmsequenzen (z.B. die Anweisungen 1 und 2) zusammengefasst werden, da hier der Kontrollfluss geradlinig und alternativlos ist. Um die Überdeckungszahlen synchron zum Quelltext zu halten, wurde auf diese Vereinfachung verzichtet.

Zeilenüberdeckung

Diese Metrik beschreibt den Anteil der vom Test ausgeführten Programmzeilen im Verhältnis zu allen Programmzeilen. Unser Beispielcode hat 10 ausführbare Zeilen.

Wir wollen diese Metrik bestimmen für den folgenden Test:

Wir übergeben als Vorbedingung ein Array mit einem Element (Wert: 100) und prüfen, ob das Ergebnis wie erwartet 0 ist. Mit jUnit wird dieser Test folgendermaßen formuliert:

Wir identifizieren ihn fortan mit:

Dieser Test führt folgende Zeilen aus:

Acht von zehn Zeilen des Programms führt unser Test aus - der Zeilenüberdeckungsgrad des Tests beträgt also 80%. ausgeführt, nicht jedoch das innere jedes if-clauses.

C = \frac{\sum{Anzahl \: getesteter \: Codezeilen} }{\sum{Anzahl \:aller \: Codezeilen}} = \frac{8}{10} = 80\%

Wenn wir den Quellcode aber umformatieren ändert sich bei gleicher Zählung der Wert:

Der Quelltext hat nur noch sechs Zeilen, von denen alle ausgeführt werden:

C = \frac{\sum{Anzahl \: getesteter \: Codezeilen}}{\sum{Anzahl \:aller \: Codezeilen}} = \frac{6}{6} = 100\%

Plötzlich haben wir 100% Zeilenüberdeckungsgrad. Diese Metrik scheint also von sehr begrenzter Aussagekraft.

Ihr Vorteil: Debugger liefert i.d.R. die Zahl der durchlaufenen Zeilen, daher ist diese Metrik einfach umzusetzen, spielt aber in der Praxis nahezu keine Rolle.

Anweisungsüberdeckung (C_0-Test, statement coverage, Knotenüberdeckung)

Der Anweisungsüberdeckungsgrad hängt nicht von der Formatierung des Codes ab: hier wird das Verhältnis der durch den Test ausgeführten Anweisungen (Operationen) zur Anzahl aller Anweisungen berechnet.

Wir betrachten den gleichen Testfall (Parameter: {100}, erwartetes Ergebnis: 0):

Manuelle Berechnung der Metrik

Im Kontrollflussgraphen markieren wir alle Anweisungsknoten, die durchlaufen wurden (Start-1-2-3-4-6-7-3-8-10-Stop):

Wir können auch hier wieder bestimmen, wie viele Anweisungen von dem Programm test aufgerufen werden.

Überdeckungsgrad:

C_0 = \frac{\sum{Anzahl \: getesteter \: Anweisungen}}{\sum{Anzahl \:aller \: Anweisungen}} = \frac{8}{10} = 80\%

Bei unserer Ermittlung über den Kontrollflußgraphen kommen wir auf 80%.

Berechnung der Metrik durch Code Coverage Tools

CodeCoverage-Tools kommen gegebenenfalls auf andere Werte als unsere händische Berechnung. JaCoCo (Java Code Coverage) gibt für unsere Beispielmethode einen Wert von 75% aus:

Das Tool gibt auch seine Werte preis: 11 Anweisungen wurden nicht erreicht, 34 hat der Test erreicht. Es ergibt sich also:

C_0 = \frac{\sum{Anzahl \: getesteter \: Anweisungen}}{\sum{Anzahl \:aller \: Anweisungen}} = \frac{34}{34+11} = 75,6\%

Aber wie kommt JaCoCo darauf? Selbst in der Detailansicht sind keine 45 Anweisungen zu erkennen:

Der Hintergrund ist, dass JaCoCo zusammengesetzte Operationen einzeln zählt, ebenso interne Operationen wie beispielsweise bei der Iteration durch Arrays. Warum ist das so wichtig zu wissen:

Ergebnisse aus CodeCoverage-Analysen unterschiedlicher Tools sind nicht vergleichbar.

Die Werte helfen lediglich beim Vergleich im selben Umfeld. Jedoch sollten 100% in allen Fällen alle Anweisungen ausführen.

Praktischer Nutzen: Testfälle ergänzen.

Wie können wir die Metrik praktisch nutzen? Wenn wir nicht 100% der Anweisungen mit unseren Tests überdeckt haben sollten wir die Testlücken analysieren. Am besten geht das natürlich mit einem Tool wie JaCoCo, dass die Codezeilen farbig markiert.

  • Kann wird der Code überhaupt die Zeilen erreichen? Gegebenenfalls können tote Codeblöcke gelöscht werden.

  • Mit welchen Vorbedingungen und Parametern kommt der Test an dieser Stelle vorbei? Im besten Fall finden sich Testfälle, der auch die übrigen Zeilen erreicht.

In der Regel sollte danach ein Anweisungsüberdeckungsgrad von 100% erreicht werden können. Für die seltenen begründeten Ausnahmen sollte innerhalb des Teams ein Workaround zu Markierung usw. entwickelt werden.

Welche Testfälle wären in unserem Beispiel erforderlich?

Um zu den beiden fehlenden Anweisungen zu kommen muss die einzelne Position über 100 und die Summe ohne Rabatt über 1000 sein. Mit einem Array mit nur einem Element über 1100 haben wir einen solchen Testfall erstellt:

Wir identifizieren in fortan mit:

Wenn wir mit diesen Werten mal durch den Kontrollfluß navigieren stellen wir obendrein fest: dieser eine Testfall alleine reicht komplett aus, um 100% Anweisungsüberdeckung zu erhalten!

Fazit zur Anweisungsüberdeckung

Der Grad der Anweisungsüberdeckung von Testfällen kann relativ leicht - auch toolgestützt - ermittelt werden. Häufig ist es relativ einfach, mit Hilfe von Tools oder Kontrollflussgraphen Testfälle zu ergänzen, um einen höheren Anweisungsüberdeckungsgrad zu erreichen. Die Aussagekraft eines hohen Anweisungsüberdeckungsgrads ist jedoch nicht sehr groß (siehe unten, Nachteile). Eine Anweisungsüberdeckung der Testfälle von 100% sollte in jedem Fall angestrebt werden, reicht jedoch alleine noch nicht als Qualitätskriterium für die Testgüte aus. Nur in begründeten Ausnahmenfällen sollte gegen eine komplette Anweisungsüberdeckung der Tests verstoßen werden.

Vorteil:

  • leicht zu ermitteln

  • findet ggf. “toten Code” (nicht erreichbare Anweisungen)

Nachteil:

  • Code gilt bereits nach einem Durchlauf als durchlaufen, auch wenn bei weiteren Durchläufen geänderte Daten vorliegen

  • Leere Zweige werden nicht gefunden ( if() ohne expliziten else() -Zweig - wie im obigen Kontrollflussgraphen )

  • keine Unterscheidung nach Wichtigkeit von Codezeilen

  • Bei Code mit Exceptionhandling ist es oft schwierig, Testfälle für eine 100%ige Anweisungsüberdeckung zu erstellen.

Zweigüberdeckung (C_1-Test, branch coverage, Kantenüberdeckung oder edge coverage)

Der Zweigüberdeckungsgrad gibt an, wie viele Prozent der Zweige des Kontrollflussgraphen durch die Tests durchlaufen werden. Werden alle Zweige durchlaufen, wurden somit auch alle Anweisungen ausgeführt. Eine komplette Zweigüberdeckung beinhaltet somit automatisch eine komplette Anweisungsüberdeckung.

Der Überdeckungsgrad wird definiert als:

C_1= \frac{überdeckte\ Zweige}{alle\ Zweige\ im\ Programm}

Um die möglichen Zweige aus dem Kontrollflußgraphen zu bestimmen können unverzweigte Kontrollflüsse zusammengefasst werden (etwa der Ablauf 1-2-3 im Beispiel). Der resultierende Kontrollflussgraph hat drei binäre Verzweigungen, also sechs Zweige:

Zweige im Beispielcode
Zweige im Beispielcode

Manuelles Bestimmen der Zweigüberdeckung über die Kanten des Kontrollflussgraphen

Wir wollen für den letzten Testfall aus dem vorigen Abschnitt die Zweigabdeckung für unsere Rabatt-Methode berechnen (Eingabeparameter: {1100}, erwartetes Ergebnis: 59). Dieser Testfall hatte ja eine Anweisungsüberdeckung von 100% erreicht:

Wir navigieren mit dem genannten Eingabewert durch den Kontrollflussgraphen. Kanten, die wir durchschreiten wurden rot hinterlegt.

(Start-1-2-3-4-5-6-7-3-8-9-10-Stop):

Kantenüberdeckung mit dem Testfall raballAusgeben({1100})==59
Kantenüberdeckung mit dem Testfall raballAusgeben({1100})==59

Die Eingangs- und die Ausgangskante werden nicht berücksichtigt. Es ergibt sich eine Kantenüberdeckung von:

C_{1, Kanten}= \frac{überdeckte\ Kanten}{alle\ Kanten\ im\ Programm} = \frac{10}{12} = 83 \%

Aus Sicht des Kontrollflusses kommt es gar nicht auf jede einzelne Kante an, sondern darauf, wie viele der Verzweigungsvarianten (also: Zweige) durch den Test überdeckt wurden. Das Beispiel verfügt über drei binäre Verzweigungen, aus denen sich 6 Zweige ergeben, vier davon erreicht unser Testfall:

C_{1, Zweig}= \frac{überdeckte\ Zweige}{alle\ Zweige\ im\ Programm} = \frac{4}{6} = 67 \%

Wir stehen vor dem gleichen Problem wie bei der Anweisungsüberdeckung: wir können sie auf unterschiedlichen Wegen bestimmen. Da uns aber nicht die konkreten Werte interessieren, sondern das Maximieren der Werte durch ergänzen von Testfällen ist es völlig unerheblich, ob wir den Zweigüberdeckungsgrad C_{1, Zweig} optimieren oder den Kantenüberdeckungsgrad C_{1, Kante}: aus 100% Kantenüberdeckung folgt 100% Zweigüberdeckung und umgekehrt.

Automatische Berechnung der Zweigüberdeckung

Codecoverage-Tools weisen die Zweigüberdeckung in der Regel auch aus. In unserem Beispiel zählt JaCoCo die beiden leeren Zweige (also die nicht implementierten else Zweige der if-Verzweigungen) und kommt folgerichtig auf 6 Zweige (Die Anzahl erhält man per Tooltip über dem grünen Balken - hier die “6”). Da die Zweige jedoch keine Anweisungen enthalten werden sie von JaCoCo als “erreicht” gewertet - was nach der definition oben schlicht falsch ist.

Wir dürfen also auch hier die Ergebnisse von unterschiedlichen Berechnungen für Zweigüberdeckungen nicht vergleichen.

Ergänzung weiterer Testfälle, um 100% Zweigabdeckung zu erhalten

Wenn wir nur mit diesem einen Testfall testen erreichen wir eine Zweigabdeckung von 67% (bei 100% Anweisungsüberdeckung).

Welche Zweige sind denn noch offen? Der Blick in den Kontrollflussgraphen verrät: Die beiden else-Zweige, also NOT(position>100) und NOT(summe-rabatt>1000) werden nicht erreicht. Es fehlen also Tests mit einer Position unter 100 und einer rabattierten Summe unter 1000. Beides hatte unser erster Testfall aus der Anweisungsüberdeckung bereits erfüllt. Eine vollständige Zweiüberdeckung erreichen wir somit mit den beiden Testfällen:

Von primitiven Zweigen

Verzweigungen sind nicht immer unabhängig voneinander Durch überlappende, sich ausschließende oder identische Bedingungen kann die Anzahl wirklich unabhängiger (primitiver) Zweige geringer sein als die Anzahl der Verzweigungen. Ein etwas überzogenes Beispiel hierfür:

Der daraus folgende Kontrollflussgraph weist vier Verzweigungen auf und 8 Zweige:

Viele der Bedingungen hängen aber voneinander ab. Die selbe Logik lässt sich folgendermaßen darstellen:

In dieser Variante liegen noch nochzwei Verzweigungen mit vier Zweigen vor.

In der Literatur werden oft nur diejenigen Zweige betrachtet, die nicht von anderen Zweigen abhängen (primitiv sind):

C_{1, primitiv} = \frac{überdeckte\ primitive\ Zweige}{alle\ primitiven\ Zweige\ im\ Programm}

In der Praxis ist eine Unterscheidung von primitiven Verzweigungen aber häufig nicht möglich. Bei einem angestrebten Zweigüberdeckungsgrad von 100% ist der Unterschied von primitiven und nicht primitiven Verzweigungen ohnehin unerheblich.

Fazit zur Zweigüberdeckung

Der Zweigüberdeckungsgrad stellt die in der Praxis relevanteste Metrik zur Überprüfung der Testgüte dar. Die Aussagekraft ist bereits deutlich höher als beim Anweisungsüberdeckungsgrad. Die Berechnung des Grads und die Bestimmung weiterer Testfälle zur Erhöhung des Überdeckungsgrads ist jedoch noch vergleichsweise einfach.

Da aus 100% Zweigüberdeckung auch eine komplette Anweisungsüberdeckung folgt, stellt eine komplette Zweigüberdeckung die Mindestanforderung an Tests dar.

Vorteil der Zweigüberdeckung:

  • deckt im Gegensatz zum Anweisungsüberdeckungsgrad auch nicht erreichbare und leere Zweige auf

Nachteile der Zweigüberdeckung

  • Abhängigkeiten zwischen Zweigen werden nicht getestet (Dazu würde Pfadüberdeckung (C_2) benötigt)

  • Weder Kombinationen von Bedingungen/Verzweigungen noch komplexere Zustände (Dateien, Betriebssystem) können einbezogen werden (Dazu würde Bedingungsüberdeckung (C_3) benötigt);

  • Schleifen werden nicht systematisch getestet, da sie nur einmal durchlaufen werden müssen.

  • Auch hier - wie bei allen folgenden Kontrollflussmetriken - bei Programmcode mit vielen Ausnahmebehandlungen (Exceptions) ist es oft nur schwer möglich, Testfälle für eine komplette Abdeckung zu formulieren.

Bedingungsüberdeckung (C_3-Test, condition coverage, Termüberdeckung)

Bei der Zweigüberdeckung steht die Verzweigung des Kontrollflusses im Vordergrund. Für zusammengesetzte Bedingungen an einer Verzweigung ist daher nur relevant, ob diese insgesamt erfüllt ist oder nicht.

Anders beim Bedingungsüberdeckungsgrad: hier wird der Fokus auf jede einzelne atomare Teilbedingungen gesetzt. Setzt sich eine Bedingung aus Teilbedingungen zusammen (z.B. (a>0) && (b>0)), so müssen Testfälle jede Teilbedingung mindestens einmal erfüllen (als true auswerten) und nicht erfüllen (als false auswerten).

C_3= \frac{überdeckte\ Bedingungen}{alle\ Bedingungen\ im\ Programm}

Unser Ausgangsbeispiel verfügt über keine zusammengesetzte Bedingung (somit wäre C_3 = C_1). Wir erweitern die Rabattregeln daher:

Der Rabatt auf die Gesamtsumme soll nur gewährt werden, wenn der Kunde über eine Rabattkarte verfügt und der geforderte Mindestbetrag vorliegt:

Wenn wir in unseren Testfällen den Parameter rabattkarteVorhanden ergänzen und mit dem Wert true belegen, erreichen wir mit folgenden beiden Testfällen wieder 100% Zweigüberdeckung:

Wir erreichen dadurch jedoch noch nicht alle Einzelbedingungen:

C_3= \frac{überdeckte\ Bedingungen}{alle\ Bedingungen\ im\ Programm} = \frac{6}{8} = 75 \%

Um jedoch auch alle Bedingungen mit true und false belegt zu haben, müssen wir einen Durchlauf mit rabattkarteVorhanden = false anfügen. Im Beispiel können wir das am einfachsten in dem Durchlauf machen, in dem der Rabatt ohnehin nicht gewährt wird:

Jetzt wäre auch der Bedingungsüberdeckungsgrad bei 100%.

Eine Erweiterung hiervon ist die Mehrfachbedingungsüberdeckung. In diesem Fall müssen nicht nur jede Teilbedingung für sich getestet werden, sondern auch alle möglichen Kombinationen:

(summe-rabatt > 1000) (rabattkarteVorhanden == true)
false false
false true
true false
true true

Um für diesen Fall auf 100% Mehrfachbedingungeüberdeckung zu kommen sind zwei weitere Testfälle nötig:

Teilweise wird die Idee der Mehrfachbedinungsüberdeckung durch logische short-circut-Operatoren wie dem “&&” ad absurdum geführt: sofern die erste Teilbedingung nicht zutrifft würde die zweite gar nicht mehr überprüft, da sie auf das Ergebnis keinen Einfluss hätte.

Fazit zur Bedingungsüberdeckung

Der Bedingungsüberdeckungstest erweitert und verfeinert den Zweigüberdeckungstest. Eine 100%ige Bedingungsüberdeckung schließt Zweig- und Anweisungsüberdeckung ein.

Bei komplexen Bedingungskonstrukten ist es ratsam, statt der Zweigüberdeckung die Testfälle auf Bedingungsbasis zu erstellen. Einige Tools weisen daher mit “branch coverage” direkt den Bedingungsüberdeckungsgrad aus.

Vorteil Bedingungsüberdeckung

  • kann bei komplexen Bedingungsverknüpfungen logische Fehler entdecken, die nach Zweigüberdeckung unentdeckt bleiben

Nachteil Bedingungsüberdeckung

  • findet nur offensichtliche Einzelbedingungen. Werden die Bedingungen in Operationen versteckt, bleiben Sie oft unentdeckt, wie in folgender Umschreibung des obigen Beispiels:
  • Mehrfachbedingungsüberdeckung ist durch kombinatorische Komplexität oft nur schwer zu erreichen.

Pfadüberdeckung (C_2-Test, path coverage)

Die bisherigen Metriken haben die unterschiedlichen Kombinationen der Verzweigungen nicht erfasst - wichtig war lediglich, dass jede Verzweigung einmal durchlaufen wurde. Bei der Pfadüberdeckung geht es darum, möglichst alle unterschiedlichen Wege zu testen, die der Kontrollfluss durch ein Programm nehmen kann.

Zwei Pfade für unser ursprüngliches Programm hatten wir in den Testfällen mit einem Element als Übergabe schon beschrieben: ###### 1. Pfad:

Mit obigem Testfall war der Anweisungsüberdeckungsgrad bereits bei 100%.

2. Pfad:

Durch die Kombination der beiden Testfälle wurde der Zweigüberdeckungsgrad auf 100% angehoben.

3. Pfad:

Es gibt noch eine weitere Variante für ein Eingabearray mit nur einem Element:

4. Pfad:

Hinzu kommt eine Variante mit leerem Eingabearray:

5. - X. Pfad:

Sobald Wiederholungen im Spiel sind gibt es eine Vielzahl an an Varianten mit unterschiedlichen Kombinationen an Eingabearrays. Es wird schnell ersichtlich, dass die Bestimmung aller möglichen Pfade bei nicht trivialen Programmstrukturen mit Wiederholungsstrukturen schnell unübersichtlich bis unmöglich wird.

Ein kleiner Auszug an weiteren Möglichkeiten für unser kurzes Beispielprogramm:

Wenn man jedoch versucht, Pfadvarianten zusammenzufassen und ähnliche Varianten mindestens einmal zu testen (siehe Beispielbezeichnungen oben), dann kann die Pfadabdeckung einen großen Beitrag dazu leisten, die Güte der Tests zu erhöhen.

Unmögliche Pfade

Eine weitere Schwierigkeit bei der Bestimmung der möglichen Pfade durch einen Programmablauf ist, dass einige nicht möglich sind, weil die nötigen Bedingungen für diese Navigation sich gegenseitig ausschließen. Ein Beispiel für so einen Pfad wäre der Pfad aus unserem Beispielprogramm, bei dem kein Element übergeben würde, aber trotzdem (summe-rabatt>1000) erfüllt sein soll. Das ist logisch nicht möglich:

Bestimmung der Metrik

Die Metrik “Pfadabdeckung” ist also in den wenigsten Fällen tatsächlich messbar - und wird daher auch von den Tools nicht mehr ausgewiesen. Sie ergibt sich - analog zu den anderen Überdeckungstestmetriken - als:

C_2= \frac{überdeckte\ Pfade}{alle\ Pfade\ durch\ das\ Programm}

Fazit zur Pfadüberdeckung

Der Pfadüberdeckungsgrad ist nur bei sehr einfachen Programmen ermittelbar, da nicht immer bekannt ist, wie viele Pfade tatsächlich möglich sind. Sollte die Pfadabdeckung in Abläufen mit Wiederholungsstrukturen gemessen werden, so muss eine Höchstzahl an Wiederholungen festgeschrieben werden.

Vorteil der Pfadüberdeckung:

  • als strukturierter Pfadtest in der Praxis hilfreich

  • kann bei der Bildung Kontrollflussorientierter Äquivalenzklassen helfen.

Nachteil der Pfadabdeckung:

  • als Metrik selten bestimmbar, da Anzahl aller Pfade oft nicht bekannt

  • keine praktische Bedeutung, da es ohne Einschränkungen zu treffen in der Praxis oft nicht durchführbar ist

  • Bei Wiederholungsstrukturen kaum einsetzbar (nur mit Beschränkung der Schleifendurchlaufanzahl)

Fazit

Die Überdeckungsgrade der Tests stellen ein erstes Indiz zur Beurteilung der Test- und Codequalität dar: mehr nicht, weniger aber auch nicht.

Eine hohe Testabdeckung garantiert nicht, dass man viele Fehler findet. Umgekehrt wird aber ein Schuh ’draus: bei einer niedrigen Testabdeckung ist die Chance sehr hoch, dass Fehler unentdeckt bleiben.

Der größte Vorteil beim Arbeiten mit Überdeckungstestmetriken ist aber: Wir analysieren den Code, versuchen zu verstehen, warum unsere Tests nicht alle Zeilen/Zweige erreichen und erhöhen somit direkt bei der Implementierung der Tests die Qualität unseres Codes.

Darüber hinaus sorgen gut abdeckende Tests dafür, dass wir keine Angst mehr vor Änderungen und Refactoring haben müssen: der Code bleibt easy to change.

Die wahre Kraft entwickeln Whiteboxtest mit Überdeckungsmetrik aber erst im Zusammenspiel von vielen Komponenten (nur die Unittest-Ebene berücksichtigt):

  • Beschreibung der Anforderungen an ein Softwaremodul oder eine Programmmethode mit Blackbox-Systematik

  • Implementierung der Methode, bis alle Testfälle bestanden werden (gerne auch zyklisch, per TDD: red, green, refactor)

  • Bestimmung der Metriken für das Modul

  • Ergänzung von Testfällen, bis die Abdeckung in gewünschtem / angemessenen Maß vorhanden ist

Quellen und offene Ressourcen (OER)

Die Ursprungstexte (als Markdown), Grafiken und zugrunde liegende Diagrammquelltexte finden sich (soweit möglich in weiterbearbeitbarer Form) in folgendem git-Repository:

https://gitlab.com/oer-informatik/qs/code-coverage.

Sie sind bei Namensnennung (oer-informatik.de) zur Nutzung als Open Education Resource (OER) freigegeben gemäß der Creative Commons Namensnennung 4.0 International Lizenz (CC BY 4.0).

Creative Commons Lizenzvertrag

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: