Kontrollflussorientierte Testverfahren
# Kontrollflussorientierte Testverfahren

Die kontrollflussorientierten Testverfahren, auch Überdeckungstests genannt, gehören zu der Gruppe der strukturorientierten Testmethoden.

Die kontrollflussorientierten Testverfahren orientieren sich am Kontrollflussgraphen des Programms. Es handelt sich bei diesen Tests um White-Box-Testverfahren, das heißt, die Struktur des Programms muss bekannt sein.

Die einzelnen Testverfahren werden mit Cx bezeichnet, wobei das „C“ für „Coverage“ steht, was so viel heißt wie die Abdeckung oder die Gesamtheit der ausgewerteten Informationen.

## Nomenklaturen

Es gibt mehrere zueinander sehr ähnlich aussehende, aber in der Bedeutung unterschiedliche Bezeichnungsarten:

* Beginnt die Bezeichnung mit kleinen c → siehe Harry M. Sneed, Mario Winter: Testen objektorientierter Software. Hanser Verlag, ISBN 3-446-21820-3.
* Beginnt die Bezeichnung mit einem großen C und steht die nun folgende Ziffer auf der Grundlinie wie das C, z. B. C4 → Ernest Wallmüller, „Software - Qualitätssicherung in der Praxis“, Hanser Verlag.
* Beginnt die Bezeichnung mit einem großen C und ist die folgende Ziffer ein Subskript, zum Beispiel $C_1,$ liegt also unterhalb der Grundlinie → siehe Standard DO-178B.

Die IEC 61508 Teil 7 und die davon abgeleiteten Anhänge der EN 50128 verwenden keine Abkürzungen für diese Metriken.

## Die unterschiedlichen Testarten

### Zeilenüberdeckungstests

Noch vor der Hierarchie der $C_x$ Testverfahren steht die von vielen Werkzeugen in der Softwareentwicklung bereitgestellte Zeilenüberdeckungskennzahl. Sie ist etwas unschärfer als $C_0,$ orientiert sich auch nicht direkt am Kontrollflussgraph, kann aber oft direkt aus den Informationen gewonnen werden, die Debugger ohnehin liefern.

Bei der Zeilenüberdeckung werden nicht die Anweisungen betrachtet, sondern nur die ausführbaren Quellcodezeilen. Beliebig viele Testfälle für

würden zu einer Zeilenüberdeckung von 100 % führen, während es für das syntaktisch und semantisch identische, aber anders formatierte Programm

nur zu einer Zeilenüberdeckung von 50 % führt.

In der Regel sind die Unterschiede in der praktischen Anwendung allerdings nicht relevant, da durch andere Maßnahmen in der Softwareentwicklung wie Kodierungsrichtlinien (engl.: "style guides") eine weitgehende Homogenisierung der Quellcodeformatierung vorliegt.

**Vorteile**

* siehe $C_0$
* einfachere technische Implementierung über die von Debuggern gelieferten Zeilennummern

**Nachteile**

* keine Standardmetrik wie $C_0$
* liefert bei syntaktisch identischen Programmen je nach Formatierung unterschiedliche Werte

### $C_0.$ Anweisungsüberdeckungstest (Statement Coverage)

Anweisungsüberdeckungstests, auch C0-Test genannt, testen jede Anweisung mindestens ein Mal. Wurde jede Anweisung in einem Programm mindestens einmal ausgeführt, spricht man von vollständiger Anweisungsüberdeckung. Wurde vollständige Anweisungsüberdeckung erreicht, dann steht fest, dass kein toter Code (Anweisungen, die niemals durchlaufen werden) im Programm existiert.

Anweisungsüberdeckungstests werden selten als Haupttestwerkzeug in einem Vollständigkeitstest eingesetzt, denn dafür sind sie in der Regel zu schwach.

**Metrik (Messung)**

Der Anweisungsüberdeckungsgrad bestimmt sich wie folgt:

    $C_{\text{0}}={\frac {\text{Anzahl der durchlaufenen Anweisungen}}{\text{Gesamtanzahl der Anweisungen}}}\cdot 100\quad \%\,$

**Vorteil**

Die Anweisungsüberdeckung bietet folgende Vorteile:

* sie deckt nicht erreichbare Anweisungen im Quellcode auf
* Anweisungsüberdeckung ist im Vergleich zu anderen Überdeckungsmaßen schnell zu erreichen

**Nachteil**

Die Anweisungsüberdeckung sollte jedoch nicht als alleiniges Testkriterium verwendet werden, denn:

* Anweisungsüberdeckung wertet jede Anweisung im Quellcode gleichgewichtig
* bei Steuerstrukturen (Schleifen, Bedingungen, …) werden die Datenabhängigkeiten nicht beachtet und
* leere Zweige werden nicht entdeckt

**Beispiel mit Quellcode**

Gegeben sei folgender Quellcode:

In diesem Fall genügt ein einziger Testfall, um eine vollständige Anweisungsüberdeckung zu erreichen, zum Beispiel x = 0, y = 2. Falls y nicht größer als x ist, wird z = y nicht ausgeführt und die vollständige Anweisungsüberdeckung wird nicht erreicht. Um alle Verzweigungen (if und else) einmal zu testen, sollte die Zweigüberdeckung als Testkriterium verwendet werden.

**Beispiel mit Kontrollflussgraphen**

Gegeben sei folgender Kontrollflussgraph:

Für diesen Kontrollflussgraphen kann die Anweisungsüberdeckung mit einem Testfall erreicht werden: {(Start, 1, 2, 3, 4, 5, Stopp)}.

### $C_1.$ Zweigüberdeckungstest (Branch Coverage)

**Allgemein**

Der Zweigüberdeckungstest $(C_1–Test;$ auch Kantenüberdeckung, Entscheidungsüberdeckungstest, Branch- oder Edge Coverage genannt) umfasst den Anweisungsüberdeckungstest vollständig. Für den $C_1–Test$ müssen strengere Kriterien erfüllt werden als beim Anweisungsüberdeckungstest. Im Bereich des kontrollflussorientierten Testens wird der Zweigüberdeckungstest als Minimalkriterium angewendet. Mit Hilfe des Zweigüberdeckungstests lassen sich nicht ausführbare Programmzweige aufspüren. Anhand dessen kann man dann Softwareteile, die oft durchlaufen werden, gezielt optimieren.

Analog zum Anweisungsüberdeckungstest wird, um die Codeabdeckung messbar zu machen, der Code in unten stehender Abbildung durch eine boolesche Hilfsvariable test instrumentiert.

Im Gegensatz zum Anweisungsüberdeckungstest durchläuft der Zweigüberdeckungstest alle Zweige. Der Zweigüberdeckungstest wird auch Entscheidungsüberdeckungstest genannt, da die Hilfsvariable mindestens einmal mit dem Wert true und false durchlaufen werden muss. In diesem Fall muss die While-Schleife mindestens zweimal durchlaufen werden. Mit dem Durchlaufen der Zweige wird auch sichergestellt, dass jeder Knoten (Anweisung) mindestens einmal ausgeführt wird. Somit wird auch das Kriterium für den Anweisungsüberdeckungstest erfüllt. Daher subsumiert der Zweigüberdeckungstest den Anweisungsüberdeckungstest. Schwierig ist es für den Zweigüberdeckungstest Testfälle zu generieren, wo Betriebssystemzustände oder Dateikonstellationen getestet werden müssen. Weiterhin ist diese Technik des Testens zum Testen von ’Schleifen’ und zusammengesetzter Entscheidungen nicht geeignet, da weder Kombinationen von Zweigen, noch kompliziert aufgebaute Entscheidungen in Betracht gezogen werden können. Hierfür müssen Erweiterungen herangezogen werden.

Die Zyklomatische Komplexität gibt an, wie viele Testfälle höchstens nötig sind, um eine Zweigüberdeckung zu erreichen.

Weitaus problematischer erweist sich das Zweigüberdeckungsmaß. In dem Fall, dass alle Knoten gleich bewertet sind, verzichtet man auf die Betrachtung der Abhängigkeiten untereinander. Dadurch entsteht kein linearer Zusammenhang zwischen der erreichten Überdeckungsrate und dem Verhältnis zwischen der Anzahl der dazu benötigten Testfälle und der eigentlichen Anzahl der Testfälle, die für die 100-prozentige Zweigüberdeckung notwendig sind. Um den Zweigüberdeckungstest zu verbessern, wird ein Zweig, der abhängig von einem anderen Zweig ist, nicht weiter berücksichtigt. Die Zweige, die nicht abhängig sind, werden als primitiv bezeichnet.

**Metrik**

Daher ergibt sich für das Überdeckungsmaß:

    $C_{\text{primitiv}}={\frac {\text{Anzahl der ausgefuehrten primitiven Zweige}}{\text{Anzahl aller primitiven Zweige}}}x100\%\,$.

**Vorteile**

* Deckt nicht erreichbare Zweige auf
* Fehlerentdeckungsrate bei ca. 33 %. Ein Fünftel davon sind Berechnungsfehler, der Rest sind Steuerflussfehler.

**Nachteile**

* Abhängigkeiten zwischen Bedingungen werden nicht berücksichtigt
* Schleifen werden nur unzureichend getestet; siehe Pfadüberdeckungstest
* komplexe Verzweigungsbedingungen werden nur schwach getestet

**Beispiele**

Gegeben sei folgender Quellcode:

Im Gegensatz zum Anweisungsüberdeckungstest ist nun mehr als ein Testfall notwendig, um eine 100%ige Zweigüberdeckung zu erreichen, da sowohl der Fall für den durchlaufenen If-Zweig, als auch der Fall für den nicht-durchlaufenen If-Zweig überprüft werden muss:

Testfall 1: x = 0, y = 2 Testfall 2: x = 2, y = 0

Wie auch im Anweisungsüberdeckungstest sind verschiedene Testfälle möglich, die das geforderte Kriterium erfüllen. Nach Ausführung stellt sich heraus, dass das Ergebnis bei beiden Testfällen der Spezifikation entspricht und der Test somit bestanden ist.

Ein weiteres Beispiel:

Gegeben sei folgender Kontrollflussgraph:

Eine Zweigüberdeckung ist {(Start, 1, 2, 3, 4, 5 Stopp), (Start, 1, 3, 5, Stopp)}.

### $C_2.$ Pfadüberdeckungstest (Path Coverage)

Beim Pfadüberdeckungstest (auch C2-Test bzw. englisch path coverage) werden im Kontrollflussgraphen die möglichen Pfade vom Startknoten bis zum Endknoten betrachtet.

**Übersicht**

* $C_2a$ – vollständiger Pfadüberdeckungstest
* $C_2b$ – Boundary-Interior Pfadüberdeckungstest
* $C_2c$ – Strukturierter Pfadüberdeckungstest

$**C_2a$ – vollständiger Pfadüberdeckungstest**

Es werden alle möglichen Pfade getestet. Problem: Bei Programmen mit Schleifen kann es extrem viele Pfade geben.

$**C_2b$ – Boundary-Interior-Pfadüberdeckungstest**

Im Prinzip wie der $C_2a-Test,$ nur dass nun die Schleifendurchläufe auf höchstens zwei reduziert werden.

Für jede Schleife gibt es zwei Gruppen von Pfaden:

$**C_2b$ – Boundary-Interior-Pfadüberdeckungstest**

**Boundary-Test**

* Jede Schleife wird
   * keinmal und
   * genau einmal betreten und alle Pfade in dem Schleifenkörper werden einmal abgearbeitet.

$**C_2b$ – Boundary-Interior-Pfadüberdeckungstest**

**Interior-Test**

* Das Schleifeninnere gilt als getestet, wenn alle Pfade, die bei zweimaligem Durchlaufen möglich sind, abgearbeitet wurden.

$**C_2c$ – Strukturierter Pfadüberdeckungstest**

Im Prinzip wie der $C_2b-Test,$ nur dass nun die Anzahl der Schleifendurchläufe auf eine vorgegebene natürliche Zahl n reduziert wird.

Vorteil

* Hohe Fehlererkennungsrate

Nachteil

* nicht ansprechbare Pfade auf Grund von Bedingungen

**Beispiel**

Gegeben sei folgender Kontrollflussgraph:

Eine Pfadüberdeckung ist {(Start, 1, 2, 3, 4, 5 Stopp), (Start, 1, 3, 5, Stopp), (Start, 1, 3, 4, 5, Stopp), (Start, 1, 2, 3, 5, Stopp)}.

### $C_3.$ Bedingungsüberdeckungstest (Condition Coverage)

Das Problem der bisherigen Überdeckungstests $(C_1-Test,$ $C_2-Test)$ ist, dass zusammengesetzte, hierarchische Bedingungen nicht ausreichend getestet werden.

* $C_3a$ – Einfachbedingungsüberdeckungstest
* $C_3b$ – Mehrfachbedingungsüberdeckungstest
* $C_3c$ – minimaler Mehrfachbedingungsüberdeckungstest
* Bewertung

$**C_3a$ – Einfachbedingungsüberdeckungstest**

Jede atomare Bedingung einer Entscheidung muss einmal mit true und einmal mit false getestet werden. Beispiel:

Eine minimale Testfallmenge, die die Einfachbedingungsüberdeckung erfüllt, ist {(a=false, b=false), (a=true, b=true)}.

$**C_3b$ – Mehrfachbedingungsüberdeckungstest**

Dieser Test betrachtet alle atomaren Bedingungen einer Bedingung. Wenn n atomare Bedingungen in der Bedingung stehen, dann werden $2^{n}$ Kombinationen gebildet.

Das heißt für das obige Beispiel, dass 4 Testfälle gebildet werden.

$**C_3c$ – minimaler Mehrfachbedingungsüberdeckungstest**

Diese Version erstellt mehr Testfälle als $C_3a$ und weniger als $C_3b,$ indem jede Bedingung (atomar und zusammengestellt) zu true und zu false evaluiert wird. Die logische Struktur wird hierbei berücksichtigt und der $C_1-Test$ (Zweigüberdeckungstest) ist vollständig in diesem Test enthalten. Ein weiterer Punkt ist, dass der $C_3c-Test$ berechenbar ist. Im obigen Beispiel wird somit der Testfall {(a=false, b=true)} oder {(a=false, b=false)} gewählt, da andernfalls die logische Struktur bereits bei der ersten Teilbedingung abbricht.

**Vorteil**

* Hohe Fehlererkennungsrate

**Nachteil**

* nicht ansprechbare Pfade auf Grund von Bedingungen

**Bewertung**

* Unvollständige Auswertung einer Bedingung durch eine Programmiersprache mit sog. short circuit evaluation wie zum Beispiel C++, C, Java, C#.

Beispiel:

Wenn a false ist, dann ist die Belegung der Variable b egal. Zum Beispiel a=false und b=null, dann passiert ein Fehler im else-Zweig

## Zusammenfassung

| | Kurzname | erfüllte Bedingung | Durchführbarkeit |
| - | - | - | - |
| Anweisungsüberdeckungstest | $C_0$ | jede Anweisung wird mindestens einmal ausgeführt | relativ einfach |
| Zweigüberdeckungstest | $C_1$ | jede Kante im Kontrollflussgraph (KFG) wird mindestens einmal durchlaufen | realistische Mindestanforderung, vertretbarer Aufwand |
| Pfadüberdeckungstest | $C_2$ | | |
| Vollständig | $C_2a$ | Alle möglichen Pfade werden durchlaufen | unmöglich bei Schleifen |
| Boundary-Interior | $C_2b$ | wie $C_2a,$ Schleifen werden jedoch nach speziellen Regeln durchlaufen | aufwendig |
| Strukturiert | $C_2c$ | wie $C_2b,$ Schleifen werden jedoch genau n-mal durchlaufen | aufwendig |
| Bedingungsüberdeckungstest | $C_3$ | | |
| Einfachbedingung | $C_3a$ | jede atomare Bedingung wird einmal mit true und false getestet | |
| Mehrfachbedingung | $C_3b$ | jede true/false Kombination der atomaren Bedingungen wird getestet | sehr hoher Aufwand |
| Minimale Mehrfachbedingung | $C_3c$ | jede atomare Bedingung und die Gesamtbedingung wird mit true und false getestet | hoher Aufwand |

## Bewertung

Die Qualität eines Tests hängt entscheidend vom gewählten Test ab: Wurde nur nach $C_0$ mit Überdeckungsgrad 100 % getestet, so ist dies trotzdem kein verlässlicher Indikator für eine fehlerfreie Software. Wurde hingegen mit $C_2$ auf 100 % getestet, würde dies ein gutes Kriterium für eine fehlerfreie bzw. -arme Software darstellen. Leider wird dieser Test wegen der kombinatorischen Explosion in der Praxis nur für sicherheitskritische Software (zum Beispiel Luftfahrt) durchgeführt.

Die zweite wichtige Größe ist der Überdeckungsgrad. Dieser ist aber nur bei Verwendung des gleichen Tests untereinander vergleichbar. Bei einem hohen Überdeckungsgrad werden mehr Fehler gefunden als bei einem niedrigen.

