Methoden der Software–Testung

Transcription

Methoden der Software–Testung
Methoden der Software–Testung
Hartmut Fritzsche
28. Oktober 2003
Die Testung von Software ist ein praktisch sehr wichtiges Feld. Wir verwenden in praktischen Beispielen Java und die UML. Für das Testen ereignisgetriebener Anwendungen
mit GUIs bilden Statecharts den Ausgangspunkt. Werkzeugunterstützung wird begleitend
besprochen.
Dresden, im Oktober 2003
ii
Inhaltsverzeichnis
1 Der Softwareentwicklungsprozess
1.1 Qualitätsanforderungen und Maßnahmen zur Qualitätssicherung
1.1.1 Spezifikationen . . . . . . . . . . . . . . . . . . . . . . .
1.1.2 Verifizierende Verfahren . . . . . . . . . . . . . . . . . .
1.1.3 Testen . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Qualitätssicherung in Vorgehensmodellen . . . . . . . . . . . . .
1.2.1 Auffinden von Konsistenz- und Äquivalenzverletzungen .
1.2.2 Generierung von Programmen . . . . . . . . . . . . . . .
1.2.3 Eine Taxonomie für Fehler . . . . . . . . . . . . . . . . .
1.3 Testen – eine produktzentrierte Sicht . . . . . . . . . . . . . . .
1.3.1 Unittest, Integrationstest, Systemtest . . . . . . . . . . .
1.3.2 Vorgehensmodell für das Testen “prozeduraler” Software
1.4 Testen – eine prozesszentrierte Sicht . . . . . . . . . . . . . . . .
1.4.1 Testplanung . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.2 Testdurchführung . . . . . . . . . . . . . . . . . . . . . .
1.4.3 Testauswertung . . . . . . . . . . . . . . . . . . . . . . .
1.4.4 Regressionstest . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
2
3
3
3
3
4
4
4
5
5
6
6
6
7
7
7
2 Methoden der Statischen Analyse
2.1 Syntaxanalyse . . . . . . . . . . .
2.2 Kontrollflussanalyse . . . . . . . .
2.2.1 Analyse von Schleifen . . .
2.3 Datenflussanalyse . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
9
9
9
10
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3 Semantik
11
4 Spezifikationsmethoden
4.1 Korrektheit von Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
13
5 Klassifizierung von Testverfahren
5.1 Klassifizierungskriterien . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.1.1 Einteilung nach verfügbarer Information . . . . . . . . . . . . . . .
5.1.2 Instrumentierung von Testobjekten . . . . . . . . . . . . . . . . . .
17
17
18
18
iii
6 Testen modularer Software
6.1 Strukturorientierte Verfahren . . . . . . . . . . . . . . . . . . . . . . . . .
6.2 Grenzwertanalyse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
19
19
7 Testen objektbasierter Software
7.1 Klassentest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.1.1 Inkrementeller Test objektbasierter Programme (Integrationstest)
7.2 JUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2.1 Testvorbereitung: Testklasse mit Spezifikationen . . . . . . . . . .
7.2.2 Testdurchführung . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.3 JUnitDoclet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
21
21
23
23
23
28
30
.
.
.
.
31
33
33
33
33
8 Testen ereignisgetriebener multimedialer Systeme
8.1 Symbolic Model Checking . . . . . . . . . . . . . .
8.2 Modale und Temporale Logiken . . . . . . . . . . .
8.3 Der Temporal Rover der Fa. Time-Rover Corp. . . .
8.4 Capture - Replay . . . . . . . . . . . . . . . . . . .
9 Die Testumgebung Jamus
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
35
iv
Kapitel 1
Der Softwareentwicklungsprozess
Softwareentwicklung ist in gewisser Weise zweidimensional“:
”
• Das zu entwickelnde Produkt wird durch hierarchisch strukturierte und mit der
Zeit wachsende Dokumentsammlungen repräsentiert. Es entstehen Dokumente unterschiedlicher Typen. Dokumente unterschiedlicher Typen stellen unterschiedliche
Sichten auf das entwickelte Produkt dar. Es gibt zusammenwirkende Komponenten.
Möglicherweise gibt es Versionierung mit Revisiionen und Varianten.
• Der Prozess der Software-Herstellung wird über der Zeit betrachtet. Er erfordert
Aktivitäten, die geplant, durchgeführt und bewertet werden müssen. Es entstehen
Dokumente, die sich nur auf diesen Prozess beziehen.
Die Aktivitäten lassen sich Entwicklungsphasen zuordnen. Die traditionelle Einteilung ist: Anforderungsanalyse, Entwurf, Implementation, Pflege und Weiterentwicklung.
In der Phase des Entwurfs entstehen Modelle (UML). Diagramme sind Repräsentationen von Entwürfen.
In der Phase der Implementation entstehen ausführbare Programme. Die zentrale, programmtechnisch optimal verarbeitbare Form eines Programmes ist allerdings dessen interne
Darstellung in Form eines Syntaxbaumes. Das Quellprogramm wird als eine mögliche Sicht
auf ein Programm verstanden. Eine andere Sicht ist z.B. der Steuerflussgraph.
Spezifikationen spielen in der Softwaretechnologie traditionell eine wichtige Rolle. Spezifikationen können in getrennten Dokumenten vorliegen oder Quellprogrammen beigefügt
sein. Aus der Sicht der Qualitätssicherung ist es zentral wichtig, Spezifikationen zu formalisieren. Vergleiche zwischen Spezifikation und Implementation können in diesem Fall
anhand der internen Darstellung automatisch erfolgen.
Bei Vorliegen sehr guter Spezifikationen können ausführbare Programme auch aus Spezifikationen generiert werden. Gewisse Annahmen werden dabei implizit getroffen.
1
1.1
Qualitätsanforderungen und Maßnahmen zur Qualitätssicherung
Qualitätsanforderungen beziehen sich sowohl auf den Prozess der Herstellung als auch auf
das herzustellende Produkt. Auf das Produkt bezogene Qualitätskriterien sind z.B. Korrektheit und Robustheit.
Qualitätssicherung (quality assurance) ist die Gesamtheit aller bewußt eingesetzten
Maßnahmen und Hilfsmittel zur Erfüllung der Qualitätsanforderungen an den Softwareentwicklungsprozess und das zu entwickelnde Softwareprodukt.
Die IEEE hat für die Software-Qualitätssicherung in den Standards 729-1983 [7], ...
relevante Begriffsdefinitionen angegeben.
Verschiedenen Rollen bei Entwicklung, Vertrieb und Nutzung von Software entsprechen
verschiedene Sichten auf Qualität.
Mas̈nahmen der Qualitätssicherung sind von verschiedenen Autoren klassifiziert und
beschrieben worden.
Einen Überblick über Test-, Analyse- u. Verifikationsverfahren für Softwaremoduln gibt
E. Wallmüller in [28]. Qualitätssicherungsmaßnahmen (QSM) werden von E. Wallmüller
eingeteilt in:
- planerisch administrative
- konstruktive
- analytische
- psychologisch orientierte
Analytische QSM können Prüfungen oder Bewertungen sein. Prüfmethoden lassen sich
einteilen in:
- statische Prüfmethoden: Analyse und Verifikation
- dynamische Prüfmethoden: Test
Statische Prüfungen umfassen
- Audits
- Reviews
- Korrektheitsbeweise
- symbolische Programmausführungen
2
1.1.1
Spezifikationen
Spezifikationen beschreiben, was ein Softwareelement tut, die Implementation legt fest, wie
es das gewünschte Verhalten erreicht.
Nur in Bezug auf eine Spezifikation sind die Begriffe Robustheit und Korrektheit zu
verstehen. Ein Programm ist korrekt, wenn es seine Spezifikation erfüllt.
1.1.2
Verifizierende Verfahren
Es wird unterschieden:
• Symbolischer Test
• Verifikationsverfahren
Verifikationsverfahren basieren in der Regel auf dem Quelltext. Bei der Verifikation wird
der Beweis der Termination als Problem ausgeklammert, man spricht von partieller Korrektheit.
1.1.3
Testen
Testen“ ist ein Begriff für dynamische Prüfungen (als analytische QSM) in Bezug auf
”
Implementationen. Bei der Prüfung von Analyse- oder Entwurfsdokumenten spricht man
dagegen von “Analysen”.
Testen meint, dass das Programm mit Eingabewerten abgearbeitet wird (Weyuker). Die
Abarbeitung kann mit verschiedenen Eingabewerte-Kombinationen wiederholt werden. Testen ist damit eine Maßnahme mit Stichprobencharakter.
Traditionelles Verständnis:
Das übersetzte, ausführbare Programm wird mit konkreten Eingabewerten versehen und
in der realen Umgebung ausgeführt.
Der Begriff Testen“ kann auch auf eine interpretative Verarbeitung bezogen werden. Spe”
ziell sind auch logische Programme und hybride Softwarestrukturen zu testen.
Testziele
Ziele der QS lassen sich aus einer allgemeineren Perspektive auch für frühe Phasen des
SEP formulieren. In der analytischen QS gelten (nach Liggesmeyer) folgende Ziele:
- Nachweis der Korrektheit
- Erkennung von Fehlern
- Vermessung oder Darstellung bestimmter Software-Eigenschaften
Die Testung zielt auf das Erkennen von Fehlern.
1.2
Qualitätssicherung in Vorgehensmodellen
Die Qualitätssicherung findet entwicklungsbegleitend statt.
3
1.2.1
Auffinden von Konsistenz- und Äquivalenzverletzungen
In der Entwicklungsphase Implementation entstehen ausführbare Programme.
1.2.2
Generierung von Programmen
1.2.3
Eine Taxonomie für Fehler
Die ANSI/IEEE-Norm 729-1983 [7] unterscheidet
• Ein Fehler (error) ist die Abweichung zwischen dem berechneten, beobachteten oder
gemessenen Wert oder einem Zustand der Betrachtungseinheit und dem entsprechenden spezifizierten oder theoretisch richtigen Wert.
• Ein Defekt (defect) ist eine Abweichung von der festgelegten (erwarteten Ausprägung
eines Merkmales einer Betrachtungseinheit. Ein Defekt mus̈ nicht unbedingt die Funktionstüchtigkeit der Betrachtungseinheit beeinträchtigen. Ein Defekt kann beispielsweise die schlechte Wartbarkeit eines Programmes sein.
• Ein Ausfall (failure) ist die Beendigung der Fähigkeit der Betrachtungseinheit, die
geforderte Funktion auszuführen.
• Eine Störung (fault) ist die Unfähigkeit der Betrachtungseinheit, ihre geforderte
Funktion auszuführen.
Definition von Kaner et al. [15]:
A mismatch between the program and its specification is an error in the program if and
only if the specification exists and is correct.
Kategorien von Software-Fehlern
Klassifizierungskriterien nach Liggesmeyer [17]:
- betroffene Qualitätseigenschaft
- Phase der Fehlerentstehung
- Phase der Fehlererkennung und -behebung
- Aufwand zur Fehlerbehebung - Art der Inkonsistenz zur Spezifikation
- Fehlerursache
- Fehlerwirkung
- Art des strukturellen Merkmals im Programmtext
Kaner et al. [15] beschreiben 13 Kategorien von Fehlern (S. 60 ff). Eine andere JKlassifizierung ist bei beizer zu finden.
4
Entwurfsfehler
Eine Gruppe basiert auf allgemeinem Wissen über Entwürfe von Softwareprodukten:
- kein Hinweis, in welchem Programm man arbeitet.
- kein Hinweis, was als nächstes zu tun ist.
- kein Hinweis, wie Programm zu beenden ist.
Eine zweite Gruppe verkörpern Verstöße gegen Anforderungsdefinitionen.
Implementationsfehler
werden während der Implementation gemacht und umfassen - Verstöße gegen die Spezifikation
- Codierungsfehler:Syntaxfehler in Bezug auf die verwendete Programmiersprache.
Defekte beim Laden eines Mediums auf der Basis Hypertext-Transfer-Protokoll
: - Verfügbarkeitsfehler
- Referenzfehler
- Übertragungsfehler
- Servicefehler
- Sicherheitsfehler
1.3
Testen – eine produktzentrierte Sicht
1.3.1
Unittest, Integrationstest, Systemtest
Es wird zunächst davon ausgegangen, dass ein Softwaresystem aus einer Struktur von
Einheiten (units) besteht.
Der Unittest beschreibt das Testen einer Einheit. Dazu gehört das Simulieren der Umgebung dieser Einheit.
Testumgebung: Eine Menge von Einheiten, die die Umgebung des Testobjektes
simulieren. Sie besteht aus einem Testrahmen und einer Menge von Pseudomoduln (Stubs).
Testrahmen: Programm, das die Benutzung des Testobjektes simuliert. Im Testrahmen wird das OUT generiert.
Der Integrationstest beschreibt, wie ausgehend von getesteten Units schrittweise ein
komplexes Softwaresystem auf der Grundlage der Bildung von Teilsystemen getestet wird.
Der Systemtest beschreibt die Testung des Gesamtsystems.
5
1.3.2
Vorgehensmodell für das Testen “prozeduraler” Software
Das Testobjekt ist ein ausführbares Programm und kann aus einem Rahmenprogramm aufgerufen werden. Es erwartet Eingangsdaten und produziert Ausgangsdaten. Der Testvorgang ist die einmalige Ausführung des Testobjektes und kann beliebig oft mit unterschiedlichen oder gleichen Daten wiederholt werden. Die einmalige Ausführung ist ein Testfall.
Die wiederholte Ausführung des Testobjektes kann automatisiert werden. Die Testdaten für
eine Reihe von Testfällen werden in einer Testsuite gesondert nach Testfällen bereitgestellt.
1.4
Testen – eine prozesszentrierte Sicht
Der Prozess des Testens gliedert sich in die Phasen Testplanung (incl. Testvorbereitung),
Testdurchführung und Testauswertung.
1.4.1
Testplanung
Im Rahmen der Testplanung sind
1. das Testverfahren festzulegen,
2. Testgegenstände zu benennen,
3. entsprechend des Verfahrens Testfälle zu bestimmen,
4. Testdaten auszuwählen (falls nicht generiert),
5. das Testendekriterium festzulegen.
Die Testplanung umfaßt:
- statische Analysen
- Generierung von Dokumenten, die Testfälle unterscheiden und beschreiben u. Testdaten
u. erwartete Ergebnisse enthalten.
Pfad möglicherweise Definition der Umgebung Es gibt Testfallbeschreibungen als Dokumente (oder auch nicht)
Testende-Kriterien
Der Prozess des Testens soll nach Erfüllung festgesetzter Kriterien für das Testende beendet
werden. Testendekriterien können sich an Testverfahren orientieren (z.B. 100 % Zweigabdeckung). E. Weyuker hat eine allgemeine Theorie der Angemessenheit entwickelt.
Kriterien:
- minimale Anzahl von Unterschieden zwischen erwarteten und gelieferten Testergebnissen.
6
- Ziele der Testverfahren wurden erreicht.
- Zeitlimit wurde erreicht oder maximale Anzahl Tests wurde erreicht.
1.4.2
Testdurchführung
Testdurchung umfaßt: Generierung von Testprogrammen, eigentliche Testdurchführung.
1.4.3
Testauswertung
Automatische Soll-Ist-Vergleiche sind wünschenswert.
Testauswertung umfaßt: Reportgenerierung.
Werkzeuge: DTS (defect tracking system) wird von [22] beschrieben
- Verfolgen des Fortschritts in Bezug auf die Testziele
- eine Liste von “unresolved Defects”
Ein Testplan beschreibt
- die Testobjekte
- die Testumgebung
Eine Testfallspezifikation beschreibt
In modifizierter Form ist das Modell auch auf die interpretative Verarbeitung anwendbar.
1.4.4
Regressionstest
Reproduzierbarkeit von Tests
Üblicherweise sind Tests nicht reproduzierbar.
Regressionstest: Wiederholung bereits durchgeführter Tests nach Software-Änderungen. Ein geeignetes Werkzeug zeichnet die Testdaten auf und gestattet die automatische
Wiederholung von Tests.
7
8
Kapitel 2
Methoden der Statischen Analyse
2.1
Syntaxanalyse
Die Syntaxanalyse spielt im Testprozess an zwei Punkten eine Rolle:
1. nur syntaktisch korrekte Programme müssen getestet werden.
2. der Syntaxbaum ist Ausgangspunkt für Instrumentierungen.
2.2
Kontrollflussanalyse
Der Kontrollfluss ist definiert als die Menge aller möglichen Ablaufpfade in einem Programm, die durch die Steuerkonstrukte der Sprache festgelegt sind. Der Kontrollfluss kann
aus dem Syntaxbaum abgeleitet werden.
2.2.1
Analyse von Schleifen
Eine Schleife umfasst eine Menge von Knoten (Basisblöcken). Alle Knoten in der Menge
sind streng verbunden. Die Menge von Knoten hat einen eindeutigen Knoten als Eingang.
Dieser Knoten (Block) ist der Kopf der Schleife.
Der Kopf muß immer durchlaufen werden, wenn man, ausgehend vom Wurzelblock außerhalb der Schleife zu einem Basisblock innerhalb der Schleife gelangen will. Mindestens eine
Kante muss von einem Block der Schleife zurück zum Kopf führen (Rückwärtskante).
Fr Schleifen, die nicht den gleichen Kopf haben, gilt: Eine Schleife, die keine andere
Schleife enthält, wird innere Schleife genannt.
Eine Tiefenordnung kann benutzt werden, um Rckwrtskanten aufzufinden: Eine Kante
von Block A nach Block B ist eine Rckwrtskante, wenn A mit einer greren Tiefennummer
als B bezeichnet ist.¡p¿
Vergabe der Knotennummern:
9
1. Durchlaufen des Baumes in Tiefenordnung
2. bereits besuchte Knoten werden markiert, damit kein Knoten mehrfach berücksichtigt
wird
3. Vergabe der Knotennummern in absteigender Reihenfolge (postorder, wenn Knoten
zuletzt besucht wird, wird ihm eine Nummer zugeteilt)
4. vom Ursprung der Rückwärtskante wird jeder besuchte Knoten in die Menge der
Schleifenblöcke aufgenommen, falls er noch nicht enthalten ist.¡br¿
Für Schleifen, die nicht den gleichen Kopf haben, gilt: Eine Schleife, die keine andere
Schleife enthält, wird innere Scheife genannt.
2.3
Datenflussanalyse
10
Kapitel 3
Semantik
P sei ein Programm.
P(x) bezeichnet das Resultat von P für die Ausführung mit einem Input-Vektor x und eine
Menge von Zuständen z. Bei Prozeduren ist die Menge der Zustände leer.
Eine Testmenge T ist eine Menge von Input-Vektoren. Bedingung: Das Programm muss
für jedes Element der Testmenge terminieren.
Test(Testfall): 1 Input-Vektor für das Programm. Tests können klassifiziert werden. Zu einer Klasse gehören die Tests, die in einer gewissen Weise gleiche Ergebnisse liefern(gleiches
Programmverhalten).
11
12
Kapitel 4
Spezifikationsmethoden
4.1
Korrektheit von Klassen
Das Vertragskonzept nach B. Meyer regelt Rechte und Pflichten der Beteiligten: Dies sind
der Kunde und der Lieferant. Meyer bezieht diese Beziehung auf Klassen.
Zusicherungen sind Spezifikationselemente, die in den Quelltext eingefügt werden, mit
compiliert werden und damit zur Ausführungszeit eines Programmes überprüft werden
können. Eine Zusicherung ist Aussage über Eigenschaften einiger Werte von ProgrammGrößen.
Zusicherungen werden u.a. bei der semantischen Spezifikation von Routinen (Methoden)
verwendet. Die von einer Routine zu erfüllende Aufgabe kann mit zwei Zusicherungen
beschrieben werden: Die Vorbedingung einer Routine bindet den Rufer (Kunde) und die
Nachbedingung bindet die Routine (den Lieferanten).
Vertrag:
Die Server-Klasse teilt den Kunden mit: Wenn Du zusagst, die Routine nur
bei erfüllten Vorbedingungen aufzurufen, sage ich zu, einen Endzustand des
Objektes zu liefern, in dem die Nachbedingungen erfüllt sind.
Abbildung 4.1: Vertragsmodell nach B. Meyer
13
Meyer notiert in Eiffel-Programmen syntaktisch Vorbedingungen nach dem Schlüsselwort
require und Nachbedingungen nach dem Schlüsselwort ensure. Vor- und Nachbedingungen sind aussagenlogische Ausdrücke.
Beispiel: Klasse Stack
push(x:T)
Vorbedingung: not (full())
Nachbedingung: not(empty()) and top() = x and nb elements = old(nb elements
+ 1)
pop():T
Vorbedingung: not (empty())
Nachbedingung: not(full()) and nb elements = old(nb elements - 1)
top():T
Vorbedingung: not (empty())
Nachbedingung:
Eine Klasseninvariante ist eine Folge von Zusicherungen, mit denen allgemeine Konsistenzbedingungen formuliert werden, die für jede Instanz gelten.
Beispiel: Klasse Stack. Die Kellerrepräsentation wird gebildet durch
max_size
nb elements
ARRAY [1 .. max size] OF T
Durch Vor- und Nachbedingungen sind bestimmte Konsistenzbedingungen nicht
ausdrückbar:
0 <= nb_elements; nb_elements <= max_size
Diese Invarianten enthalten nur Attribute, es sind auch Invarianten möglich,
die Funktionsaufrufe enthalten.
empty() = (nb_elements = 0)
Meyer notiert Klasseninvarianten in Klassendefinitionen nach dem Symbol invariant auf
dem Niveau von Instanzvariablen und Methoden.
Klasseninvarianten müssen in jedem stabilen Zustand eines Objekts erfllt sein. Ein
Objekt ist in einem stabilen Zustand nach der Objekterzeugung sowie vor und nach jedem
von außen kommenden Aufruf einer Routine (d.h. Methode).
Korrektheit von Klassen:
Seien P und Q Zusicherungen und A eine Anweisung oder eine Folge von Anweisungen.
14
Die Notation P {A} Q bedeutet: Wenn A in einem Zustand beginnt, in dem P wahr ist, ist
Q nach Ausführung von A wahr (wir folgen hier nicht der von Meyer verwendeten Notation
sondern verwenden die Notation von Hoare).
Seien C eine Klasse und INV ihre Klasseninvariante. Die Vor- und Nachbedingungen
einer Routine r der Klasse C werden mit prer und postr bezeichnet. Fehlen Vor- und
Nachbedingungen, sind die Werte true einzusetzen. Der Rumpf der Routine heißt Br . Ein
Konstruktor von C heißt c. Def aultc ist die Zusicherung, dass die Attribute von C die
Vorbelegungswerte ihrer jeweiligen Typen tragen.
Eine Klasse C heißt korrekt in Bezug auf ihre Zusicherungen genau dann, wenn:
1. für jede exportierte Routine r (außer Konstruktoren) und jede Menge gültiger Argumente xr gilt:
INV and prer (xr ) Br INV and postr
2. für jede gültige Menge von Argumenten xc für c gilt:
Def aultc and prec (xc ) Bc INV postc
Schleifeninvarianten: Eine korrekte Invariante für eine Schleife ist eine Zusicherung,
die nach Schleifeninitialisierung erfüllt ist, und deren Gültigkeit durch jede Iteration des
Schleifenkörpers erhalten bleibt.
Schleifenvarianten: Eine Schleifenvariante stellt sicher, dass die Schleife terminiert.
15
16
Kapitel 5
Klassifizierung von Testverfahren
Testverfahren beschreiben die Vorgehensweise zur Erreichung bestimmter Testziele, wobei
unterschiedliche Strategien verfolgt werden können. Sie sind sehr vielfältig und werden
nachfolgend klassifiziert.
5.1
Klassifizierungskriterien
Testverfahren lassen sich nach unterschiedlichen Kriterien klassifizieren.
- Software-Entwicklungsprozess: Unittest/inkrementelles Testen/Systemtest
- Strukturierung des Softwareproduktes: objektbasiert, logikbasiert, funktional, interaktiv, reaktiv, ...
- verfügbare Information: formale oder informale Spezifikation, z.B. API (Blackbox)/Quellen
(Whitebox)
- vergleichende Verfahren:
– Versionen
– diversifizierende Verfahren: Mutationstest
- Fehlerkategorien
- Strukturierung von Eingabedaten(Bereichstests):
- Instrumentierung des Testobjektes:
- Phasen im Testprozeß: Regressionstests
Die Strategie eines anzuwendenden Testverfahrens basiert auf einer Kombination bez.
Klassifizierungskriterien.
17
5.1.1
Einteilung nach verfügbarer Information
White-box-Test Mit dem Begriff Strukturtest(White-box-Test) werden Testverfahrengefasst, bei denen Tests auf der Grundlage der Kenntnis der (Quell-)Programm-Struktur
des Testobjekts ermittelt werden.
Es werden kontrollflussorientierte und datenflussorientierte Verfahren unterschieden.
Funktionstest Als Referenz dient die funktionale Spezifikation. Auswahl von Tests nach
Randbedingungen (boundary conditions). Die bevorzugten Testfälle liegen am Rand einer
Klasse.
5.1.2
Instrumentierung von Testobjekten
18
Kapitel 6
Testen modularer Software
6.1
Strukturorientierte Verfahren
6.2
Grenzwertanalyse
19
20
Kapitel 7
Testen objektbasierter Software
Überblicksliteratur: H.M. Sneed [21].
Objektorientierte Grundkonzepte (Datenkapselung, Vererbung, Polymorphie) reduzieren den Testaufwand nicht. Datenkapselung behindert die Beobachtbarkeit. Um uneingeschränkten Zugriff auf Instanzvariablen zu erreichen, deklariert die zu testende Klasse eine
bestimmte andere Testklasse als friend [5].
Adaption traditioneller Testverfahren: Aufruf einer zustandsverändernden Methode
analog zu traditioneller Variablenzuweisung. Aufruf einer nicht zustandsverändernden Methode analog Auslesen eines Variablenwertes. Durch diese Neu-Interpretation können traditionelle Überdeckungsmas̈e auf den objektorientierten Test übertragen werden.
7.1
Klassentest
Der Unittest ist bei objektbasierter Software ein Klassentest. Eine Spezifik besteht darin,
dass Klassen
1. Struktur und Verhalten von zustandsbehafteten Instanzen beschreiben und
2. selbst zustandsbehaftete Laufzeitobjekte sind.
Es wird zunächst nur der zweite Aspekt betrachtet. Eine Klasse kann durch einen sequentielle Maschine(SM) beschrieben werden. Das dazugehörige Zustandsdiagramm ist für jede
Instanz interpretierbar.
Eine SM ist durch zwei Strukturen bestimmt: durch die Transitions-Struktur und durch
die Output-Struktur. Ein Automat A ist eine SM ohne Betrachtung der Output-Struktur.
Zustandsübergänge werden durch Stimuli (d.h. Inputs) herbeigeführt. Ein Stimulus ist
1. ein Methodenaufruf mit einer Kombination von Parameterwerten oder
2. ein einen Zustandsübergang auslösendes Event.
21
Die Klasse selbst beruht auf einem aktuellen Automaten A=(SA ,ΣA ,δA )
mit δ: S × Σ → S
Das Verhalten der Instanzen einer Klasse ist durch die bei der Klasse definierten Operationen beschrieben. Operationen können Methoden, Konstruktoren oder Destruktoren sein.
Destruktoren werden im folgenden nicht betrachtet. Im Kontext eines Instanz-Objektes
können Operationen ausgeführt werden, die
1. geerbt
2. geerbt und überschrieben
3. neu definiert
sein können. Methoden aller drei Arten können wiederum in zustandsverändernde und
nicht zustandsverändernde eingeteilt werden.
Erforderlich ist das Testen aller Methoden einer Klasse einzeln in einer bestimmten
Reihenfolge. Begründungen für Auswahl und Reihenfolgen zu testender Methoden liefern
[19] und [23]. Der Test einzelner Methoden ist aufwndig und daher unpraktikabel, weil pro
Methode eine künstliche Klasse als Testumgebung implementiert werden müßte (behauptet Liggesmeyer, [17]). Berard schlägt vor, zuerst nicht-zustandsverändernde Methoden zu
testen.
Der Test von Methodenkombinationen ist zusätzlich notwendig. Bestimmte Arten von
Fehlern sind nur im Zusammenwirken von Methoden erkennbar. Beispiel: Doppelt verkettete Liste.
Besonderheit: Es gibt zu testende Objekte, die nur zur Laufzeit existieren.
Zustandsbasierter Test von Klassen
Mit dieser Thematik beschäftigen sich Turner und Robson [23],[24],[25],[26],[27] und Graham [6].
Klassen werden durch endliche Automaten modelliert. Auf der Basis des Automatenmodells einer Klasse werden Teställe generiert.
Der aktuelle Zustand eines Objektes ist die Kombination aller Werte aller Attribute zu
einem bestimmten Zeitpunkt. Ein Teilzustand (substate) ist der Wert eines bestimmten
Attributes zu einem bestimmten Zeitpunkt.
Es wird weiter zwischen zwei Arten von Teilzustandswerten unterschieden:
1. Specific substate values
2. general substate values: Eine Gruppe von Werten
22
Vor und nach jeder Operation ist der Zustand des Objekts zu überprüfen. Test von
Änderungen in Attributwerten.
Ein Testfall für ein zu testendes Objekt (OUT) umfasst
1. die Erzeugung eines bestimmten Zustandes des OUT,
2. den Aufruf einer Operation (Methode),
3. die Validierung des durch die Operation herbeigeführten Zustandes des OUT.
Bestimmte Fehler lassen sich erst durch den kombinierten Aufruf von Methoden feststellen.
Methodenkombinationen:
Seien m1, m2 und m3 Methoden einer Klasse. Eine Methodenkombination mkb ist ein
Tupel mk=(mi,mj,mk) mit
1. ein Aufruf von m2 in m1
2. das aufeinanderfolgende Ausführen von m1 und m2 in m3
3. das Auftreten eines Parameterausdrucks (verschachteltes Ausführen von m1 und m2
in m3)
7.1.1
Inkrementeller Test objektbasierter Programme (Integrationstest)
Inkrementeller Test von Klassenhierarchien bietet [6] Es wird vorausgesetzt, dass Klassen
einzeln getestet sind.
Lakos: Testen von C++.
Es müssen Vererbungs- und Benutztbeziehungen zwischen Klassen festgestellt werden.
7.2
JUnit
Zur Unterstützung des Testprozesses bei der Entwicklung von Java-Software dient das Tool
JUnit von Kent Beck und Erich Gamma. Das Tool JUnit unterstützt Klassentests.
7.2.1
Testvorbereitung: Testklasse mit Spezifikationen
Neben einer zu testenden Klasse myclass wird eine Klasse mit dem Namen myclassTest
benötigt, die die Funktion des Testrahmens übernimmt (vgl. Abb. 7.1 ). Es wird empfohlen,
die Klasse myclassTest im selben Package abzulegen wie die Klasse myclass.
23
TestCase
+assert()
+assertEquals()
myclass
1
1
myclassTest
Abbildung 7.1: JUnit-Frame
Jede Methode mi Test von myclassTest ist eine Testmethode und dient zum Testen
einer public-Methode mi von myclass. Testmethoden sind parameterlos.
Der Körper jeder Methode mi Test enthält mindestens einen Aufruf der Methode mi .
Die Klasse myclassTest muss von der Klasse TestCase abgeleitet sein. In den Methoden
von myclassTest werden Vergleiche mittels assert-Methoden durchgeführt. Dabei werden
Operationen benötigt, die Aussagen treffen können, ob zwei Objekte gleich sind.
Zwei Objekte sind gleich, wenn sie strukturgleich sind oder wenn sie identisch sind.
Mittels der von TestCase geerbten booleschen Methode assert kann geprüft werden,
ob ein übergebenes Argument den Wert true liefert. Mittels assertEquals kann geprüft
werden, ob zwei übergebene Argumente gleich sind.
Die Ausführung der Testfall-Methoden erfolgt automatisch im Tool JUnit. Falls assert
nicht true liefert, wird ein Fehler im Tool JUnit registriert und signalisiert.
Das folgende Beispielprogramm Count wurde der Veröffentlichung [?] entnommen. Die Klasse Count realisiert einen Zähler, der von 1 bis 4 zählt.
package JavaTests;
import junit.framework.*;
public class Count {
24
TestCase
+assert()
+assertEquals()
Count
1
1
CountTest
-iStore : int
+next()
+nextTest()
+reset()
+resetTest()
CountUnderTest
Abbildung 7.2: JUnit-Frame für Count
25
protected int iStore;
public Count(){
iStore = 1;
}
public void reset(){
iStore = 1;
}
public void next(){
iStore++;
if (iStore > 4) reset();
}
}
Zu Testzwecken wird eine Subklasse CountUnderTest gebildet. Die zu testende
Klasse soll zu Zwecken der Testung nicht verändert werden. Die Eigenschaften
der Klasse Count ändern sich nicht, wenn eine Klasse abgeleitet wird.
package JavaTests;
import junit.framework.*;
class CountUT extends Count{
public CountUT(int state){
iStore=state;
}
public boolean equals(Object obj){
if (obj instanceof Count){
Count aCount = (Count)obj;
return aCount.iStore == iStore;
}
return false;
}
boolean test_for_substate1(int sValue){
int svCur;
switch(iStore) {
case 1: svCur=1; break;
case 2:
case 3: svCur=2; break;
case 4: svCur=3; break;
default:svCur=0; break;
}
26
return (svCur == sValue);
}
protected int iStoreMirror;
boolean test_forsubstate1_Change(boolean bInit){
if (bInit == true)
iStoreMirror = iStore;
return (iStore != iStoreMirror);
}
}
Der Zustand des Objektes muss vor und nach jedem Methodenaufruf überprüft werden. Das
Mirror-Attribut wird initialisiert, wenn test_for_substate1_Change mit true aufgerufen
wird.Ein Aufruf mit false prüft, ob sich der Wert des entsprechenden Attributs geändert
hat.
Die Klasse CountUTTest muss Zugriff auf die Attribute der zu testenden Klasse haben.
package JavaTests;
import junit.framework.*;
public class CountUTTest extends TestCase{
public void main(String [] args){
junit.textui.TestRunner.run (CountUTTest.class);
}
/*
public static Test suite ( ) {
TestSuite suite= new TestSuite();
suite.addTest( new CountUTTest("testNext"));
return suite;
}
*/
public void testNext(){
CountUT c1 = new CountUT(1);
c1.next();
CountUT c2 = new CountUT(2);
// assertEquals(c1,c1);
assertEquals(c1,c2);
assertTrue(c1.test_for_substate1(2));
System.out.println("fertig");
}
}
27
// failed
Abbildung 7.3: JUnit-TestRunner
7.2.2
Testdurchführung
Die Installation von JUnit ist einfach. Die gelieferte .zip-Datei ist lediglich zu entpacken.
Es wird ein Verzeichnis junit3.8.1 angelegt. Im CLASSPATH müssen dieses Verzeichnis
und das darin enthaltene junit.jar-File enthalten sein. Beispiel:
C:\Programme\junit3.8.1\junit.jar;c:\Programme\junit3.8.1
Zur Ausführung von Testfällen sind die Quelldateien zu übersetzen und der Testrunner zu
starten. Den Testrunner gibt es in drei Varianten. Nachfolgend wird der Testrunner mit
grafischer Oberfläche gestartet und auf die mitgelieferten Beispieltests angewendet:
C:\Workstation\J>java junit.awtui.TestRunner JavaTests.CountUTTest
Dabei ist junit.samples.AllTests eine Klasse mit folgendem Inhalt:
28
public class AllTests {
public static void main (String[] args) {
junit.textui.TestRunner.run (suite());
}
public static Test suite ( ) {
TestSuite suite= new TestSuite("All JUnit Tests");
suite.addTest(VectorTest.suite());
suite.addTest(new TestSuite(junit.samples.money.MoneyTest.class));
suite.addTest(junit.tests.AllTests.suite());
return suite;
}
}
Einzelne Testflle sind in einer Testsuite zusammengestellt. Eine Testsuite kann eine Kollektion von Tests ausführen. TestCase und TestSuite implementieren das Interface Test.
Das Inteface Test definiert die Methoden, um einen Test ablaufen zu lassen.
Das Testen einer Methode ist in folgenden Schritten durchzuführen:
1. Erzeugen der am Test beteiligten Objekte, auch der erwarteten Objekte.
2. Ausführen der Methode
3. Verifizieren der Ergebnisse
Zur Ausführung spezifizierter Testfälle gibt es zwei Möglichkeiten:
1. definieren einzelner Testfälle
2. definieren einer Test-Suite
Zur Ausführung einzelner Testfälle gibt es einen statischen und einen dynamischen Weg:
1. statischer Weg:
Überschreiben der von TestCase geerbten Methode runTest in der Klasse myclassTest.
TestCase test= new CountTest("Testfall1"){
public void runTest(){
nextTest();
}
}
2. dynamischer Weg:
In diesem Fall wird auf Reflexion zurückgegriffen. Der Nutzer gibt als Testfallbezeichnung nur den Namen der Testmethode an.
29
Ausführen einer Testsuite erfolgt mit dem TestRunner:
java junit.swingui.TestRunner junit.samples.AllTests
Zusammenstellen von einzelnen Testfällen zu einer TestSuite:
7.3
JUnitDoclet
JUnit kann in Kombination mit dem Tool JUnitDoclet eingesetzt werden. JUnitDoclet
arbeitet als plugin für das JavaDoc tool. Es generiert aber anstelle von HTML-Text Testklassen und Methoden.
30
Kapitel 8
Testen ereignisgetriebener
multimedialer Systeme
Der Ausgangspunkt sind UML-Diagramme. Es gibt Statecharts. Fr Statecharts gibt es eine
interne Reprsentation. Es wird ein Interpeter geschaffen. Lassen sich aus UML-Statecharts
Quellcodes generieren?
Beispiel: DuscheMVC
/** Java class "Leitung.java" generated from Poseidon for UML.
* Poseidon for UML is developed by <A HREF="http://www.gentleware.com">Gentleware</A
* Generated with <A HREF="http://jakarta.apache.org/velocity/">velocity</A> template
*/
import java.util.*;
/**
* <p>
*
* </p>
*/
public class Leitung {
///////////////////////////////////////
// attributes
/**
* <p>
* Represents ...
* </p>
*/
public int zust;
31
hahn_auf
geschlossen
auslauf_frei
hahn_zu
hahn_zu
hahn_zu
hebel_runter
hebel_rauf
dusche_frei
Abbildung 8.1: Beispiel DuscheMVC
///////////////////////////////////////
//states
public synchronized boolean isInGeschlossen(){
return true;
}
public synchronized boolean isInAuslauf_frei(){
return true;
}
public synchronized boolean isInDusche_frei(){
return true;
}
} // end Leitung
Softwareprodukte dieser Art sind Information Retrieval-Applikationen.
Die Systeme sind ereignisgesteuert. Die Wirkung von Ereignissen hängt vom Zustand
ab.
Teststrategien für Endliche Automaten: Kapitel 11 in [2].
32
8.1
Symbolic Model Checking
Verifizierung von Sicherheitseigenschaften ereignisgesteuerter Systeme.
Annahme: Wenn eine Formel (safty assertion) im Modell wahr ist, wird die saftyassetion vom System erfüllt.
Safty assertions werden in Computational Tree Logic formuliert.
8.2
Modale und Temporale Logiken
Die Temporale Logik ist eine Erweiterung der Modalen Logik.
8.3
Der Temporal Rover der Fa. Time-Rover Corp.
Der Temporal Rover ist eine Java-Applikation. Er dient der Verifizierung temporallogischer
Aussagen über das temporale Verhalten von Programmen.
8.4
Capture - Replay
33
34
Kapitel 9
Die Testumgebung Jamus
Das Tool besitzt eine grafische Benutzeroberfläche. Jamus integriert das Tool JUnit.
35
36
Literaturverzeichnis
[1] B. Beizer: Software System Testing and Quality Assurance. International Thomson
Computer Press 1996
[2] B. Beizer: Software Testing Techniques. International Thomson Computer Press 1990
[3] MY-F. Chen, D.S. Rosenblum, K-P. Vo: TestTube: A System for Selective Regression
Testing. In: ICSE-16
[4] P.T.Devanbu, D.S. Rosenblum, A.L.Wolf: Automated Construction of Testing and
Analysis Tools. In: ICSE-16
[5] M. Dorman: Unit Testing of C++-Objects. In EuroStar’93 Software Testing Analysis
& Review, Okt. 1993
[6] J.A. Graham, A.C.T. Drakeford, C.T. Turner: The Verification, Validation and Testing
of object Oriented Systems BT Technology Journal Vol 11, No. 3, 1993
[7] ANSI/IEEE Std 729-1983: IEEE Standard Glossary of Software Engineering Terminology. IEEE, New York 1983
[8] ANSI/IEEE Std 829-1983: IEEE Standard for Software Test Documentation. IEEE,
New York 1983
[9] ANSI/IEEE Std 730-1984: IEEE Standard for Software Quality Assurance Plans.
IEEE, New York 1984
[10] ANSI/IEEE Std 830-1984: IEEE Guide to Software Requirements Specifications. IEEE, New York 1984
[11] ANSI/IEEE Std 1008-1985: IEEE Standard for Software Unit Testing. IEEE, New
York 1985
[12] ANSI/IEEE Std 983-1986: IEEE Standard for Software Quality Assurance Planning.
IEEE, New York 1986
[13] ANSI/IEEE Std 1012-1986: IEEE Standard for Software Verification and Valdation
Plans. IEEE, New York 1986
37
[14] ANSI/IEEE Std 1016-1987: IEEE Standard for Recommended Practice for Software
Design Descriptions. IEEE, New York 1987
[15] C. Kaner, J. Falk, H.Q. Nguyen: Testing Computer Software. Van Nostrand Reinhold,
1993
[16] W.Kowalk: System - Modell - Programm: Vom Goto zur objektorientierten Programmierung. Spektrum akademischer Verlag GmbH, 1996
[17] P. Liggesmeyer: Modultest und Modulverifikation. BI Wissenschaftsverlag, 1990
[18] G. Myer. The Art of Software-Testing
[19] J.Overbeck: Integration Testing for Object-Oriented Software. Dissertation, Dptm. for
Information Systems Vienna University of Technology, Vienna 1994
[20] P. Rüppel: Ein generisches Testwerkzeug für den objektorientierten Softwaretest - neue
Möglichkeiten zur Testunterstützung. TU Berlin, FB Informatik - Softwaretechnik,
10.7.1996
[21] H.M. Sneed: Objektorientiertes Testen. Informatik-Spektrum 18(1995):6-12, Springer
Verlag, 1995
[22] J.Su, P.R.Ritter: Expereance in Testing the Motif Interface. IEEE Transactions on
Software Engineering, März 1991, pp. 26-33
[23] C.D. Turner, D.J. Robson: The Testing of Object-Oriented Programs. Technical Report TR-13/92, Computer Science Division, School of Engineering and Computer
Sciences (SECS), University of Durham, England, 1993
[24] C.D. Turner, D.J. Robson: A Suite of Tools for the State-Based Testing of ObjectOriented Programs. Technical Report TR-14/92, Computer Science Division, School
of Engineering and Computer Sciences (SECS), University of Durham, England, 1993
[25] C.D. Turner, D.J. Robson: State-Based Testing and Inheritance. Technical Report
TR-1/93, Computer Science Division, School of Engineering and Computer Sciences
(SECS), University of Durham, England, 1993
[26] C.D. Turner, D.J. Robson: Guidance for the Testing of Object-Oriented Programs.
Technical Report TR-2/93, Computer Science Division, School of Engineering and
Computer Sciences (SECS), University of Durham, England, 1993
[27] C.D. Turner, D.J. Robson: State-Based Testing of Object-Oriented Programs. In: D.
Card (ed.) Proceedings of the Conference on Software Maintenance (CSM-93) IEEE
Computer Society Press, September 1993
[28] E.Wallmüller: Software-Qualitätssicherung in der Praxis. Carl Hanser Verlag, 1990
38
[29] E.J. Weyuker: Axiomating Software Test Data Adequacy. IEEE Transactions on Software Engineering, Vol. SE-12, Dezember 1986, pp. 1128-1138
39