Codeüberdeckung
Kurz gesagt: Die Anweisungsüberdeckung fragt, ob jede Codezeile gelaufen ist. Die Zweigüberdeckung fragt, ob jede Entscheidung in beide Richtungen gegangen ist. Bei der Lieferfunktion braucht die erste 2 Tests, die zweite 3. 100 % Zweigüberdeckung schließen 100 % Anweisungsüberdeckung ein, aber nicht umgekehrt. Und keine Überdeckung sagt, dass das Ergebnis geprüft wurde oder dass die Grenze an der richtigen Stelle liegt.
Problem
100 % Überdeckung, eine falsche Gebühr
Der Entwickler lässt die Lieferungstests mit einem Werkzeug laufen, das zählt, welche Zeilen gelaufen sind. Die Tests sind zwei: 0 € und 60 €. Der Bericht sagt: 100 % der Zeilen.
Am nächsten Tag zahlen Kunden mit einem Warenkorb von 30 € 4,99 € Lieferkosten statt 4,90 €. Die Zeile mit der Gebühr ist gelaufen. Kein Test hat gesehen, was darin stand.
Die Idee
Die Whitebox
Bisher kamen die Tests aus der Regel: Das ist die Blackbox. Whitebox-Verfahren nehmen die Tests aus dem Code: welche Zeilen er hat und welche Pfade das Programm nehmen kann.
Für diese Lektion musst du keinen Code schreiben können. Du musst die Lieferfunktion lesen können. Die Sprache wählst du selbst, in den Tabs:
static String delivery(double total) { if (total <= 0) { return "abgelehnt"; } double fee = 4.90; if (total >= 50) { fee = 0; } return String.format("%.2f €", fee);}static string Delivery(double total){ if (total <= 0) { return "abgelehnt"; } double fee = 4.90; if (total >= 50) { fee = 0; } return $"{fee:F2} €";}def delivery(total): if total <= 0: return "abgelehnt" fee = 4.90 if total >= 50: fee = 0 return f"{fee:.2f} €"function delivery(total) { if (total <= 0) { return "abgelehnt"; } let fee = 4.9; if (total >= 50) { fee = 0; } return `${fee.toFixed(2)} €`;}Die Funktion hat zwei Entscheidungen (Stellen, an denen das Programm einen Pfad wählt): „der Warenkorbwert ist 0 oder weniger“ und „der Warenkorbwert ist 50 oder mehr“. Die zweite ist ein „if“ ohne „else“: Wenn sie falsch ist, überspringt das Programm einfach die Zeile, die die Gebühr auf null setzt.
Die Idee
Anweisungsüberdeckung
Die Anweisungsüberdeckung fragt: Wie viele der Zeilen, die etwas tun, hat mindestens ein Test ausgeführt?
Hier sind die zwei Tests vom Anfang als Aufrufe der Funktion, mit ihrer Ausgabe:
System.out.println("0 € → " + delivery(0));System.out.println("60 € → " + delivery(60));0 € → abgelehnt60 € → 0.00 €Console.WriteLine("0 € → " + Delivery(0));Console.WriteLine("60 € → " + Delivery(60));0 € → abgelehnt60 € → 0.00 €print("0 € →", delivery(0))print("60 € →", delivery(60))0 € → abgelehnt60 € → 0.00 €console.log("0 € → " + delivery(0));console.log("60 € → " + delivery(60));0 € → abgelehnt60 € → 0.00 €- 0 € geht durch die erste Prüfung und gibt „abgelehnt“ zurück.
- 60 € geht durch alles andere: Die Gebühr wird 4,90, dann macht die Prüfung „ab 50“ sie zu 0, dann die Rückgabe.
Jede Zeile ist gelaufen. Die Anweisungsüberdeckung beträgt 100 %, mit zwei Tests.
Die Idee
Zweigüberdeckung
Die Zweigüberdeckung fragt etwas anderes: Ist jede Entscheidung in beide Richtungen gegangen?
Hier gibt es vier Zweige: die erste Prüfung wahr und falsch, die zweite wahr und falsch. 0 € nimmt die erste als wahr. 60 € nimmt die erste als falsch und die zweite als wahr. Niemand nimmt die zweite als falsch: Das ist der leere Pfad eines „if“ ohne „else“. Die Überdeckung ist 3 von 4, also 75 %.
Ein dritter Test, mit kostenpflichtiger Lieferung, schließt die Lücke:
System.out.println("0 € → " + delivery(0));System.out.println("30 € → " + delivery(30));System.out.println("60 € → " + delivery(60));0 € → abgelehnt30 € → 4.90 €60 € → 0.00 €Console.WriteLine("0 € → " + Delivery(0));Console.WriteLine("30 € → " + Delivery(30));Console.WriteLine("60 € → " + Delivery(60));0 € → abgelehnt30 € → 4.90 €60 € → 0.00 €print("0 € →", delivery(0))print("30 € →", delivery(30))print("60 € →", delivery(60))0 € → abgelehnt30 € → 4.90 €60 € → 0.00 €console.log("0 € → " + delivery(0));console.log("30 € → " + delivery(30));console.log("60 € → " + delivery(60));0 € → abgelehnt30 € → 4.90 €60 € → 0.00 €Drei Tests, 100 % der Zweige. Und die Antwort auf die Frage der Lektion: 100 % Zweigüberdeckung der Lieferfunktion brauchen 3 Tests.
Genauer, für Neugierige
Der ISTQB CTFL v4.0.1 beschreibt beide Maße in Abschnitt 4.3: Anweisungstest und Zweigtest. Die Überdeckung ist die Anzahl der ausgeführten Anweisungen (oder genommenen Zweige) im Verhältnis zu allen, in Prozent. Der Lehrplan nennt auch ihr Verhältnis: 100 % Zweigüberdeckung schließen 100 % Anweisungsüberdeckung ein, aber nicht umgekehrt.
Es gibt auch strengere Maße: die Bedingungsüberdeckung (jede Bedingung in einer Entscheidung wurde wahr und falsch gesehen) und MC/DC (jede Bedingung hat gezeigt, dass sie allein die Entscheidung ändern kann). MC/DC wird für die kritischste Software in der Luftfahrt verlangt. Im CTFL v4.0 stehen sie nicht.
Die Überdeckung wird mit einem Werkzeug gemessen: JaCoCo für Java, coverlet für C#, coverage.py für Python, c8 oder das in Node eingebaute für JavaScript. Die Tests laufen, und das Werkzeug markiert, welche Zeilen und Zweige genommen wurden.
Probier es aus
Wie viele Tests?
Die Idee
Warum 100 % Anweisungen die Gebühr übersahen
Hier ist die Funktion vom Anfang, mit der Gebühr 4,99:
static String delivery(double total) { if (total <= 0) { return "abgelehnt"; } double fee = 4.99; if (total >= 50) { fee = 0; } return String.format("%.2f €", fee);}static string Delivery(double total){ if (total <= 0) { return "abgelehnt"; } double fee = 4.99; if (total >= 50) { fee = 0; } return $"{fee:F2} €";}def delivery(total): if total <= 0: return "abgelehnt" fee = 4.99 if total >= 50: fee = 0 return f"{fee:.2f} €"function delivery(total) { if (total <= 0) { return "abgelehnt"; } let fee = 4.99; if (total >= 50) { fee = 0; } return `${fee.toFixed(2)} €`;}Mit den zwei Anweisungstests ist die Ausgabe dieselbe wie bei der richtigen Funktion:
System.out.println("0 € → " + delivery(0));System.out.println("60 € → " + delivery(60));0 € → abgelehnt60 € → 0.00 €Console.WriteLine("0 € → " + Delivery(0));Console.WriteLine("60 € → " + Delivery(60));0 € → abgelehnt60 € → 0.00 €print("0 € →", delivery(0))print("60 € →", delivery(60))0 € → abgelehnt60 € → 0.00 €console.log("0 € → " + delivery(0));console.log("60 € → " + delivery(60));0 € → abgelehnt60 € → 0.00 €Bei 60 € läuft die Zeile mit der Gebühr, aber gleich danach wird die Gebühr 0. Der falsche Wert erreicht die Ausgabe nie. Gelaufen heißt nicht geprüft.
Der dritte Test, der für die volle Zweigüberdeckung, nimmt den leeren Pfad und zeigt die Gebühr:
System.out.println("0 € → " + delivery(0));System.out.println("30 € → " + delivery(30));System.out.println("60 € → " + delivery(60));0 € → abgelehnt30 € → 4.99 €60 € → 0.00 €Console.WriteLine("0 € → " + Delivery(0));Console.WriteLine("30 € → " + Delivery(30));Console.WriteLine("60 € → " + Delivery(60));0 € → abgelehnt30 € → 4.99 €60 € → 0.00 €print("0 € →", delivery(0))print("30 € →", delivery(30))print("60 € →", delivery(60))0 € → abgelehnt30 € → 4.99 €60 € → 0.00 €console.log("0 € → " + delivery(0));console.log("30 € → " + delivery(30));console.log("60 € → " + delivery(60));0 € → abgelehnt30 € → 4.99 €60 € → 0.00 €Deshalb ist die Zweigüberdeckung stärker. Der leere Pfad eines „if“ ist genau die Stelle, an der ein Wert aus der Zeile darüber den Kunden erreicht.
Die Idee
Was Überdeckung nicht sagt
Hier ist die Prüfung des Warenkorb-Limits: Das Hinzufügen wird abgelehnt, wenn es weniger als 1 Stück ist oder wenn der Warenkorb mehr als 10 enthalten würde.
static boolean canAdd(int inCart, int add) { if (add < 1 || inCart + add > 10) { return false; } return true;}static bool CanAdd(int inCart, int add){ if (add < 1 || inCart + add > 10) { return false; } return true;}def can_add(in_cart, add): if add < 1 or in_cart + add > 10: return False return Truefunction canAdd(inCart, add) { if (add < 1 || inCart + add > 10) { return false; } return true;}Zwei Aufrufe geben 100 % der Zweige: (5, 0) geht durch „abgelehnt“, (2, 3) durch „angenommen“.
System.out.println(canAdd(5, 0));System.out.println(canAdd(2, 3));falsetrueConsole.WriteLine(CanAdd(5, 0));Console.WriteLine(CanAdd(2, 3));FalseTrueprint(can_add(5, 0))print(can_add(2, 3))FalseTrueconsole.log(canAdd(5, 0));console.log(canAdd(2, 3));falsetrueÄndere jetzt „> 10“ zu „> 11“. Dieselben zwei Tests bestehen, die Überdeckung ist immer noch 100 %, und das 11. Stück kommt in den Warenkorb. Die Überdeckung misst, was gelaufen ist, nicht wie nah an der Grenze. Der Test (10, 1) aus der Lektion über Grenzwerte fängt den Fehler.
Und etwas, das keine Überdeckung sehen kann: fehlender Code. Wenn die Prüfung auf einen leeren Warenkorb nie geschrieben wurde, bleibt keine Zeile ungedeckt. 100 % Überdeckung von Code, der nicht tut, was gebraucht wird, sind immer noch 100 %. Deshalb ersetzt die Whitebox die Blackbox nicht. Überdeckung sagt, wo es keine Tests gibt, nicht dass die Tests gut sind.
Übung
Die Überdeckung des Limits
1. Zwei Bedingungen, eine Entscheidung. Die Fragen gehen um die Limitprüfung oben.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Warum früh · Äquivalenzklassen · Grenzwerte · Entscheidungstabellen
- Geht weiter in: Erfahrungsbasiertes Testen
- Aufgaben zum Thema: Testentwurfsverfahren