Seminar: Messen und Prüfen von Software, WS 2001/02
Glass-Box-Test:
Datenstrukturtest
Inhaltsverzeichnis
Zusammenfassung 3
Einleitung 4
Der Datenstrukturtest 5
Einordnung des Testverfahrens 5
Anvisierte Fehler 5
Die Defs- und Uses-Kriterien 5
Testverfahren 7
Das all defs-Kriterium 7
Das all c-uses-Kriterium 7
Das all p-uses-Kriterium 8
Das all c-uses / some p-uses-Kriterium 8
Das all p-uses / some c-uses-Kriterium 8
Das all uses-Kriterium 8
Beispiel 2 9
Erfüllen der Kriterien anhand von Beispiel 2 10
Leistungsfähigkeit der Tests 11
Quellenangabe 12
Tabellen- und Grafikverzeichnis
Kontrollflussgraph zu Beispiel 1 6
Tabelle 1: Variablenzugriffe in Knoten und Kanten von Beispiel 1 7
Tabelle 2: dcu und dpu von Beispiel 1 7
Übersicht: Subsumtionsrelarionen 8
Kontrollflussgraph Beispiel 2 9
Tabelle 3: Variablenzugriffe in Knoten und Kanten von Beispiel 2 9/10
Tabelle 4: dcu und dpu von Beispiel 2 10
Zusammenfassung
Der Datenstrukturtest ist ein Glassboxtest. Er orientiert sich an den Zugriffen auf Programmvariablen, d.h. an Definitionen von (Wertzuweisungen zu) Variablen, an berechnenden Zugriffen auf Variablen und an prädikativen Zugriffen auf Variablen (IF THEN-Statements). Von diesem Test existieren sechs Ausprägungen, wobei einige andere subsumieren.
Einleitung
Das systematische Testen von Programmen ist ein wichtiger Bestandteil in der Entwicklung von Software, da es fast unmöglich ist fehlerfreie Software zu erzeugen. Durch das Testen sollen möglichst viele Fehler gefunden werden. Allerdings existieren selbst bei relativ kleinen Programmen unendlich viele potentielle Testfälle. Irgendwie muss eine Auswahl getroffen werden. Eine zufällige Auswahl der Testfälle scheidet aus, da es sich dabei per Definition um eine unsystematische Auswahl handelt. Wichtige Fragen sind also: Welche Testfälle soll ich wählen? Nach welchen Kriterien werden diese Testfälle ausgewählt?
Die verschiedenen Testarten – und damit auch die Auswahlkriterien für die Testfälle - lassen sich grundsätzlich in Black-Box- und Glass-Box-Verfahren (White-Box) unterteilen, wobei die Trennlinie nicht immer scharf ist.
Black-Box-Verfahren stützen sich nur auf die Programm-Spezifikation, der Programmcode an sich spielt keine Rolle bei der Auswahl der Testfälle. Anders bei den Glass-Box-Verfahren, hier werden die Testfälle durch die Programmstruktur – also den Code selbst – bestimmt.
In dieser Arbeit wird der Datenstrukturtest, bzw. sechs Ausprägungen dieses Tests, etwas näher betrachtet. Dieser Test - ein Glass-Box-Test - orientiert sich an den Zugriffen auf die Variablen, d.h. am Datenfluss und nicht am Kontrollfluss eines Programms.
Der Datenstrukturtest
Einordnung des Testverfahrens
Beim Datenstrukturtest handelt es sich um ein strukturorientiertes, dynamisches
Testverfahren. Strukturorientiert, weil die Programmstruktur sichtbar sein muss (Glass-Box) und dynamisch, da die Tests am übersetzten Programm ausgeführt werden. Allerdings werden viele Fehler bereits bei der Testvorbereitung gefunden, also beim statischen Teil des
Verfahrens.
Anvisierte Fehler
Jeder Verfahren hat Stärken und Schwächen im Finden bestimmter Fehlertypen. Welche Fehlerkategorien werden mit diesem Testverfahren speziell anvisiert?
- -Verwendung von undefinierten Variablen
- -Definierte Variablen die nicht verwendet werden
- -Variablen, die vor ihrer Verwendung mehrfach definiert werden
Die Defs- und Uses-Kriterien
Die Zugriffe auf Programmvariablen bilden den Kern des Datenstrukturtests.
Variablenzugriffe können in drei Kategorien unterteilt werden: Definition, berechnender und prädikativer Zugriff.
- def: Dies steht für Definition von Variablen. Gemeint ist hier eine Wertzuweisung.
Bsp.:
pi := 3.14;
- c-use: Ist ein berechnender Zugriff auf eine Variable (computional-use). Auch die Ausgabe eines Wertes gilt als c-use. Bsp.:
Alter := Date - Geburtstag;
(Auf Geburtstag und Date erfolgt ein c-use-Zugriff, auf Alter ein def-Zugriff) - p-use: Hier wird eine Variable zur Bildung eines Wahrheitswertes (prädikative
Benutzung) gebraucht. Bsp.:
IF Gewicht >= MaxGewicht THEN
Beispiel 1:
1 PROCEDURE MinMax (VAR Min: Integer; VAR Max: Integer);
2 VAR Hilf: Integer;
3 BEGIN
4 IF Min > Max THEN
5 Hilf := Max;
6 Max := Min;
7 Min := Hilf
8 END;
9 END MinMax;
def
def c-use
p-use
Kontrollflussgraph zu Beispiel 1:
nstart
nin Zeile 1; def(Min), def(Max)
n1
Zeile 4; p-use(Min), p-use(Max)
n2 Zeilen 5-7; def(Min), def(Max), def(Hilf), c-use(Min), c-use(Max), c-use(Hilf)
nout Export von Min und Max; c-use(Min), c-use(Max) nfinal
In Beispiel 1 werden alle drei Arten von Variablenzugriffen auf Min und Max angewandt.
Diesen Variablen werden zweimal Werte zugewiesen (def), beim Prozedur-Aufruf {nin} und im IF-Block {n2}. Beide Werte werden im IF-Block für die Bedingung (p-use) {(n1,nout), (n1,n2)} und für Berechnungen (c-use) {n2} benutzt.
Alle def und c-uses beziehen sich auf einen bestimmten Knoten des Kontrollflussgraphen, p- uses werden hingegen mit den abgehenden Kanten eines prädikativen Zugriffs identifiziert.
Dies hat rein praktische Gründe, da dadurch die Notation der Testverfahren vereinfacht wird.
Weitere wichtige Begriffe:
- Definitionsfreier Pfad:
Ein Pfad p = (n1,...,nm), der in keinem seiner Knoten eine Definition (Wertzuweisung) einer Variablen x enthält, heisst definitionsfrei bezüglich x.
- Globaler c-use:
Ein berechnender Variablenzugriff ist global, falls ihm keine def im selben Block vorangeht.
- Lokaler c-use:
Nicht globaler c-use.
- Globale def:
Eine Wertzuweisung ist global, falls ein definitionsfreier Pfad zu einem c-use oder p- use besteht.
- Lokale def:
Nicht globale def.
- Erreichen einer Benutzung:
Eine def(x) in Knoten ni erreicht ein c-use in Knoten nj,falls ein Subpfad (ni)p(nj) existiert, und p definitionsfrei bezüglich x ist. D.h. es ist möglich von ni zu nj zu kommen, ohne dass dabei die Variable x verändert wird.
Eine def(x) in Knoten ni erreicht ein p-use in Kante (nj,nk),falls ein Subpfad (ni)p(nj,nk) existiert, und p definitionsfrei bezüglich x ist.
- Dcu(x,ni):
Das ist die Menge aller Knoten nj in denen ein berechnender Zugriff (c-use) auf x existiert und zu denen von ni aus ein definitionsfreier Pfad führt.
- Dpu(x,ni):
Das ist die Menge aller Kanten (nj,nk) in denen ein prädikativer Zugriff (p-use) auf x existiert und zu denen von ni aus ein definitionsfreier Pfad führt.
Vorgehensweise zur Bestimmung der Testfälle
Zuerst muss für jeden Knoten ni des Kontrollflussgraphen die Menge der in ihm global definierten Variablen (def(ni)) und der globalen berechnenden Zugriffe (c-use(ni)) bestimmt werden. Analog werden für die Kanten (ni,nj) die p-use(ni,nj) bestimmt.
Knoten ni Def(ni) c-use(ni)
nin {Min, Max} {}
n1 {} {Min, Max}
n2 {Min, Max} {Min, Max}
nout {} {Min, Max}
Kanten (ni,nj) p-use(ni,nj)
(n1,n2) {Min, Max}
(n1,nout) {Min, Max}
Tabelle 1: Variablenzugriffe in Knoten und Kanten von Beispiel 1
Variable x Knoten ni dcu(x, ni) dpu(x, ni)
Min nin {n2, nout} {(n1,n2), (n1,nout)}
Min n2 nout {}
Max nin {n2, nout} {(n1,n2), (n1,nout)}
Max n2 nout {}
Tabelle 2: dcu und dpu von Beispiel 1
Nun können Verfahren zum Festlegen von Testfällen bestimmt werden.
Testverfahren
Mit dem bis jetzt Bekannten können eine Reihe von Kriterien bestimmt werden, die eine Menge von Testpfaden – aus diesen werden die Testfälle abgeleitet - erfüllen sollte. Einige der unten beschriebenen Kriterien werden von anderen Kriterien subsumiert (siehe Übersicht:
Subsumtionsrelationen). Alle Kriterien werden später anhand von Beispielen genauer erläutert.
Das all defs-Kriterium
Jede Definition einer Variablen muss in mindestens einer Berechnung oder in einem Prädikat benutzt werden. Dieses Kriterium ist erfüllt, wenn für jeden Knoten ni und jede Variable x def(ni) mindestens ein definitionsfreier Pfad bezüglich x von ni zu einem Element von dcu(x,ni) oder dpu(x,ni) ausgeführt wird. In Beispiel 1 wird das Programm durch den Testpfad (nstart, nin, n1, n2, nout, nfinal) hinreichend getestet.
Das all c-uses-Kriterium
Jeder berechnende Zugriff auf eine Variable muss mit jeder Wertzuweisung von der aus der Zugriff erreicht werden kann mindestens einmal ausgeführt werden. Die Pfade (nstart, nin, n1, n2, nout, nfinal) und (nstart, nin, n1, nout, nfinal) müssen in Beispiel 1 abgedeckt werden.
Das all p-uses-Kriterium
Jeder prädikative Zugriff auf eine Variable muss mit jeder Wertzuweisung von der aus der Zugriff erreicht werden kann mindestens einmal ausgeführt werden. Dieses Kriterium erfüllt die Zweigüberdeckung, da bei jeder Verzweigung (prädikativer Zugriff) alle Zweige einmal ausgeführt werden müssen. In Beispiel 1 wird dieses Kriterium erfüllt wenn folgende Kanten abgedeckt werden: (n1, n2) und (n1, nout). Dafür braucht es die beiden Pfade (nstart, nin, n1, n2, nout, nfinal) und (nstart, nin, n1, nout, nfinal).
Das all c-uses / some p-uses-Kriterium
Dieses Kriterium erfordert die Erfüllung des all c-uses-Kriterium. Zusätzlich muss für jede Definition die noch nicht von einem Testpfad abgedeckt ist ein definitionsfreier Pfad zu einem prädikativen Zugriff ausgeführt werden. Dieses Kriterium subsumiert das all-defs- und das all c-uses-Kriterium.
Das all p-uses / some c-uses-Kriterium
Das ist so etwas wie das Spiegelbild des all c-uses / some p-uses-Kriteriums. Definitionen, die noch nicht durch das all p-uses-Kriterium geprüft werden, müssen in einem berechnenden Zugriff benutzt werden. Dieses Kriterium subsumiert das all-defs- und das all p-uses- Kriterium.
Das all uses-Kriterium
Dies ist die Kombination der beiden vorangegangenen Kriterien und subsumiert folglich beide.
all uses
all c-uses / some p-uses all p-uses /
some c-uses
all c-uses
all p-uses all defs
Zweig- überdeckung
Anweisungs- überdeckung
A subsumiert B A
B
Beispiel 2
Dieses Beispielprogramm liest Zeichen ein solange diese Grosbuchstaben sind. Rückgabewert ist die Anzahl der Grossbuchstaben und die Anzahl der Vokale.
1 Import von VokalAnzahl und Gesamtzahl
2 Lies(Zeichen)
3 WHILE ((Zeichen>=’A’) AND (Zeichen<=’Z’)) AND (Gesamtzahl < MAX(CARDINAL))) DO
4 Gesamtzahl := Gesamtzahl + 1;
5 IF ((Zeichen=’A’) OR (Zeichen=’E’) OR (Zeichen=’I’) OR (Zeichen=’O’) OR (Zeichen=’U’)) THEN
6 VokalAnzahl := VokalAnzahl + 1
7 END
8 Lies(Zeichen)
9 END;
10 Export von VokalAnzahl und Gesamtzahl
nstart
nin Zeile 1: Import
n1 Zeile 2: Lies(Zeichen)
n2 Zeile 3: While Grossbuchstaben
n3 Zeile 4: Erhöhe Gesamtzahl
n4 Zeile 5: If Vokal
n5 Zeile 6: Erhöhe VokalAnzahl
n6 Zeile 8: Lies(Zeichen)
nout Zeile 10: Export
nfinal
Knoten def(ni) c-use(ni)
nin {Gesamtzahl, VokalAnzahl} {}
n1 {Zeichen} {}
n2 {} {}
n3 {Gesamtzahl} {Gesamtzahl}
n4 {} {}
n5 {VokalAnzahl} {VokalAnzahl}
Kontrollflussgraph Beispiel 2
Kanten p-use(ni,nj)
(n2,n3) {Zeichen, Gesamtzahl}
(n2,nout) {Zeichen, Gesamtzahl}
(n4,n5) {Zeichen}
(n4,n6) {Zeichen}
Tabelle 3: Variablenzugriffe in Knoten und Kanten von Beispiel 2
Variable x Knoten ni dcu(x, ni) dpu(x, ni) Zeichen n1
n6
{(n2,n3),(n2,nout),(n4,n5),(n4,n6)}
{(n2,n3),(n2,nout),(n4,n5),(n4,n6)} {}
{}
Gesamtzahl nin
n3
{(n2,n3),(n2,nout)}
{(n2,n3),(n2,nout)}
{n3,nout} {n3,nout} VokalAnzahl nin
n5
{}
{}
{n5,nout} {n5,nout} Tabelle 4: dcu und dpu von Beispiel 2
Erfüllen der Kriterien anhand von Beispiel 2 Das all defs-Kriterium:
Die geforderten Testpfade müssen jeweils mindestens einen definitionsfreien Pfad von einer Variablendefinition zu einer Benutzung der Variablen abdecken.
Testpfad: nstart, nin, n1, n2, n3, n4, n5, n6, n2, nout, nfinal
Testfall: Aufruf mit Gesamtzahl = 0 1. Zeichen =’A’,’1’
Dieser eine Testfall genügt bereits um das all defs-Kriterium zu erfüllen. Die Kante (n4,n6) wird nicht geprüft.
Das all c-use-Kriterium:
Hier müssen alle Pfade abgedeckt werden, die von einer Definition einen berechnenden Zugriff auf eine Variable erreichen.
Testpfade: nstart, nin, n1, n2, n3, n4, n5, n6, n2, n3, n4, n5, n6, n2, nout, nfinal
nstart, nin, n1, n2, nout, nfinal
Testfälle: Aufruf mit Gesamtzahl = 0 1. Zeichen =’A’,’E’,’1’
2. Zeichen =’a’
Das all p-use-Kriterium:
Dieses Kriterium verlangt Pfade von allen Definitionen zu allen erreichten Kanten mit prädikativer Benutzung der Variablen.
Testpfade: nstart, nin, n1, n2, n3, n4, n5, n6, n2, n3, n4, n5, n6, n2, n3, n4, n6, n2, nout, nfinal
nstart, nin, n1, n2, nout, nfinal
nstart, nin, n1, n2, n3, n4, n6, n2, nout, nfinal
Testfälle: Aufruf mit Gesamtzahl = 0 1. Zeichen =’A’,’E’,’B’,’1’
2. Zeichen =’a’
3. Zeichen =’C’,’2’
Das all c-use / some p-use-Kriterium:
Von Zeichen liegt kein berechnender Zugriff vor. Der prädikative Zugriff wird jedoch durch die Testpfade des all c-use-Kriteriums bereits abgedeckt.
Das all p-use / some c-use-Kriterium:
Dieses Kriterium wird in diesem Beispiel mit denselben Testpfaden bzw. Testfällen erfüllt wie das all p-use-Kriterium. Die Variable AnzahlVokale, die nirgends prädikativ genutzt wird, wird im ersten Testpfad berechnend genutzt.
Das all uses-Kriterium:
Dieses Kriterium ist eine Kombination der vorangegangenen Kriterien. Deshalb können die Testpfade der anderen kombiniert werden.
Testpfade: nstart, nin, n1, n2, n3, n4, n5, n6, n2, n3, n4, n5, n6, n2, n3, n4, n6, n2, nout, nfinal
nstart, nin, n1, n2, nout, nfinal
nstart, nin, n1, n2, n3, n4, n6, n2, nout, nfinal
Testfälle: Aufruf mit Gesamtzahl = 0 1. Zeichen =’A’,’E’,’B’,’1’
2. Zeichen =’a’
3. Zeichen =’C’,’2’
Leistungsfähigkeit der Tests
Die Angaben über die Leistungsfähigkeit dieser Testverfahren beziehen sich alle auf eine Studie von /Girgis, Woodward 86/. Die Studie vergleicht das all defs-Kriterium, das all c-use- Kriterium und das all p-use-Kriterium untereinander und mit anderen Verfahren. Alle drei genannten Verfahren zusammen (entspricht all uses) spüren etwa 70% der Programmfehler auf. Jedes alleine hat ungefähr folgende Trefferquote und Vorteile:
- all c-use-Kriterium (48%), entdeckt Berechnungsfehler am besten
- all p-use-Kriterium (34%), entdeckt Kontrollflussfehler am besten (79%) - all defs-Kriterium (24%), erkennt keine Kontrollflussfehler
Quellenangabe
- Peter Liggesmeyer, Modultest und Modulverifikation, ISBN 3-411-14361-4
- http://www.aifb.uni-karlsruhe.de/Lehrangebot/Sommer2001/qm/stqm4-Testtheorie.pdf - http://sigma.uni-paderborn.de/lehre/itp/itpss00/atac-tut.pdf