6 Systematisches Testen von Programmen Testen Untersuchung des Source-Codes nach Fehlern und Anomalien Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.1/24
Untersuchung des Source-Codes Code-Inspectionen (strenge Regeln, Entscheidung über die Freigabe des Source-Codes) Eine Anomalie wie if ((options == ( WCLONE WALL)) && (current->uid = 0)) retval = -EINVAL; hat in dem Source-Code für ein sicheres System nichts zu suchen! Walk-Throughs (weniger strikte Regeln) Programming in Pairs Formale Verifikation ( in ein paar Wochen) Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.2/24
Was ist Testen? Testen ist ein Prozess, ein Programm mit der Absicht auszuführen, Fehler zu finden. Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.3/24
Testpsychologie Psychologisches Problem: Die Autoren eines Programms sind schlechte Tester für ihr eigenes Programm. Man muss Fehler finden wollen! Bedingte Abhilfe: Test-Driven Developement (zuerst die Black-Box-Tests, erst danach die Implementation). Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.4/24
Unterschiedliche Tests Unterscheidung nach Methodik: White-box Testverfahren Black-Box Testverfahren Unterscheidung nach Situation/Zeitpunkt: Modul- bzw. Komponententest Integrations- und Aktzeptanztest (werden in dieser Vorlesung nicht weiter behandelt) Wichtiger Begriff: Regressionstest Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.5/24
Regressionstest Batterie von alten Tests. Vorbedingung für die Freigabe einer Änderung Wenn ein neuer Fehler gefunden wird: Zuerst neuer Testfall für den Regressionstest. Dann Behebung des Fehlers. Denn: Fehler neigen dazu, wiederholt zu werden! Vermutlich die wichtigste Art von Tests (bei langlebiger Software). Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.6/24
Testfall Beschreibung einer Eingabe(-Bedingung) mit einem erwarteten Ergebnis Zweck Vorbedingung Erwartetes Ergebnis Aufräumarbeiten Bei der Durchführung des Tests: Vergleich des tatsächlichem mit dem Erwarteten Ergebnis, Protokollierung. Ggf. Verknüpfung mehrerer Testfälle zu einer Kette. Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.7/24
function Setze(Tag: Tag_Typ; Monat: Monatsname; Jahr: Jahr_Typ) return Datum is Beispiel 1 Dat: Datum := (Tag, Monat, Jahr); begin case Monat is when Januar Maerz Mai Juli August Oktober Dezember => null; Anzahl der Tage passt when Februar => if Jahr mod 4 = 0 and... then Schaltjahr if Tag not in 1.. 29 then raise falsches_datum; end if; else keinschaltjahr if Tag not in 1.. 28 then raise falsches_datum; end if; end if; when others => if Tag not in 1.. 30 then raise falsches_datum; end if; end case; return Dat; end Setze; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.8/24
Beispiel 2 function Fibonacci(X: Positive) return Positive is begin Kind, Vater, Opa: Positive; if X <= 2 then else return 1; Vater := 1 Opa := 1; for Generation in 3.. X loop Kind := Vater + Opa; Opa := Vater; Vater := Kind; end loop; return Kind; end if; end Fibonacci; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.9/24
White-Box Tests Anweisungsüberdeckung: Jeder Knoten des Kontrollflussgraphen wird mindestens einmal ausgeführt. Zweigabdeckung: Jede Kante des Kontrollflussgraphen wird mindestens einmal ausgeführt. Pfadabdeckung: Jeder Pfad im Kontrollflussgraphen wird mindestens einmal ausgeführt (theoretisches Kriterium, da meistens unendlich viele Pfade). Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.10/24
Kontrollflussgraph Knoten: Ausführbare Anweisung (Zuweisung, Prozeduraufruf) zusätzlich je ein Start- und Zielknoten ggf. Zusammenfassung mehrerer zusammenhängender Anweisungen zu einem Knoten Kante: möglicher Programmfluss zwischen zwei Anweisungen Pad: Kombination von Kanten, die vom Start- zum Zielknoten laufen Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.11/24
Zu den Beispielen Kontrollflussgraph Menge von Testfällen, die alle Anweisungen überdeckt Menge von Testfällen, die alle Zweige abdeckt Menge von Testfällen, die alle Pfade abdeckt Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.12/24
Mutationstesten Kleine Änderungen ( Mutationen ) am Quelltext. Jeder Mutant enthält nur eine Abweichung, z.b. Ersetzen eines durch ein <, eines and durch ein or,... Vorzeichenfehler, Auskommentieren einzelner Statements, Vertauschen der Reihenfolge einzelner Statements,... Wenn irgend eine Mutation den Test besteht, hat man vermutlich nicht genug Testfälle, oder die falschen Testfälle. Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.13/24
Black-Box Tests Äquivalenzklassenbildung (sowohl für gültige, als auch für ungültige Eingaben) Grenzwertanalyse Welche Äquivalenzklassen für Beispiel 1: function Setze(Tag: Tag_Typ; Monat: Monatsname; Jahr: Jahr_Typ) return Datum; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.14/24
Beispiel Kaffeeautomat package Kaffee_Automat is type Zustand is private; type Benutzer_Handlung is (Cent10, Cent20, Knopf); type Aktionen is (Muenze_Angenommen, Muenze_Abgelehnt, Alle_Muenzen_Zurueck, Gib_Kaffee_Aus); Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.15/24
Kaffeeautomat (2) procedure Initialisiere (X: in out Zustand); procedure Agiere(X: in out Zustand; Eingabe: Benutzer_Handlung; Aktion: out Aktionen); private type Zustand is range 0.. 50; end Kaffee_Automat; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.16/24
Wie testen wir den Kaffeeautomaten? Spezifikationen, wie für derartige Automaten üblich. Insbesondere: Eingabe 10-Cent und 20-Cent Münzen Kaffee für 50 Cent, kein Überbezahlen Geldrückgabeknopf Gibt es Äquivalenzklassen, die wir hier nutzen können? Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.17/24
Manuelles Testen >>./test_dm 29 Februar 2004 Tag: 29; Monat: FEBRUAR Jahr: 2004 >>./test_dm 29 Februar 2003 raised DATUM_MANAGER.FALSCHES_DATUM : test_dm.adb Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.18/24
Automatisches Testen Manuelles Testen ist arbeitsaufwändig, langweilig und... fehlerträchtig. Um die Reproduzierbarkeit der Test zu gewährleisten, muss bei jeder Wiederholung des Tests (z.b. Regressionstest), der gleiche Input eingegeben werden. Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.19/24
Der Test-Driver Generator tg Ein extrem einfach zu handhabendes Werkzeug, um Test-Driver zu erzeugen ist der Test-Driver Generator tg. In der Vorlesung dient tg als Beispiel für ein Werkzeug beim Testen. Für anspruchsvollere Aufgaben würde man z.b. AUnit verwenden. Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.20/24
Arbeitsweise von tg Eingabe von tg ist ein Test-Script (z.b. beispiel.ts), Ausgabe der Quelltest eines Ada-Programms (z.b. beispiel.adb). (Man achte darauf, sich nicht das Programm, das man gerade testen möchte, zu überschreiben!) Typische Test-Scripts bestehen aus 1. Einer globalen Kontext-Vereinbarung, bestehend aus with und use -Klauseln für Ada.Text_IO und dem getesteten Modul, und 2. einzelnen Testfällen. Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.21/24
Die Syntax eines tg Test-Scripts Context with Ada.Text_Io; use Ada.Text_IO; with Datum_Manager; use Datum_Manager; ***** 30. Juni 2003 (kein Schaltjahr) Define Dat: Datum; Test Dat := Setze(31, Juni, 2003); Pass (Tag(Dat)=30) and (Monat(Dat)=Juni) and (Jahr(Dat)=2003) ***** 31. Juni 2003 (kein Schaltjahr) Define Dat: Datum; Test Dat := Setze(31, Juni, 2003); Pass exception Falsches_Datum Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.22/24
Die Syntax eines Testfalls Testfall-Name, Define-, Test- und Pass-Vereinbarungen: ***** Mai, alle Tage, 2003 (kein Schaltjahr) Define Fehler: Natural := 0; Dat: Datum; Test for T in Tag_Typ loop Dat := Setze(T, Mai, 2003); if (Tag(Dat) /= T) or (Monat(Dat) /= Mai) or (Jahr(Dat) /= 2003) then Fehler := Fehler + 1; end if; end loop; Pass Fehler = 0 Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.23/24
Verhalten eines Test-Scripts Endresulat Pass alle Testfälle: Pass. Der Test ist erfolgreich Endresultat Fail. (1) pass. (2) pass. (3) pass. (4) Februar, 1..28, 2003 (kein Schaltjahr) Script name: dat_man.ts ; Line:34...FAIL. (path => was taken, but predicate is FALSE) (5) pass. (6) pass. (7) pass. Script name dat_man.ts ; total test result: FAIL. Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 6 p.24/24