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