• Keine Ergebnisse gefunden

Theoretische Ansätze zum Klassentest

Im Dokument Das Praxishandbuch für den Test (Seite 184-187)

Unterschiede zwischen Klassentest und Modultest Zweck des Klassentests

6.4 Theoretische Ansätze zum Klassentest

Robert Binder unterscheidet zwei Arten des Klassentests [Bin96b]:

• implementierungsbezogen und

• spezifikationsbezogen.

6.4.1 Implementierungsbezogener Klassentest

Der implementierungsbezogene Klassentest geht vom Quellcode der Klasse aus.

Dort werden die Methoden, Verzweigungen, Operationsaufrufe und Parameter identifiziert und der Test so konstruiert, dass die Methoden mit allen Parameter-kombinationen aufgerufen, alle Verzweigungen durchlaufen und alle Operations-aufrufe betätigt werden. Binder betont, dass dies eigentlich für jede Instanzierung der Klasse bzw. für jede Objektausprägung erforderlich wäre. Da der Klassentest irgendwie abzugrenzen ist, müssen die Aufrufe fremder Operationen irgendwo und irgendwie abgefangen werden. Binder gibt dafür jedoch kein Rezept. Der Klassen-test wird von einem KlassenKlassen-testtreiber aus getrieben, der die Methoden der Klasse unter Test auslöst [Bin94b].

Stellvertretend für den implementierungsbezogenen Ansatz stellen wir im Folgen-den die MethoFolgen-den von Smith und Robson, McGregor und Sykes, Thuy, Hoffman und Strooper, Turner und Robson, Kung, Parrish, D’Souza und Leblanc, Overbeck, McCabe sowie Harrold und Rothermel vor.

Smith und Robson haben die FOOT-Methode vorgeschlagen – „framework for object-oriented testing“. Danach werden Testfälle automatisch aus dem Source-Code generiert. Vererbung wird simuliert, indem alle geerbten Operationen und Daten in den Quellcode der Zielklasse eingebaut werden – class flattening. Fremde Operationen werden durch Stubs simuliert. Getestet wird, ob der Code in sich kon-sistent ist und ob alle Objektzustände dem Sollzustand entsprechen. Schnittstellen nach außen werden nicht getestet, da die Klasse unter Test nach außen völlig abge-schottet ist. Bestätigt wird eigentlich nur die Ausführbarkeit und logische Korrekt-heit der einzelnen Methoden [SmRo92].

McGregor und Sykes setzen voraus, dass der Entwickler Vor- und Nachbedingun-gen in den Klassencode einbaut. Die VorbedingunNachbedingun-gen prüfen am Eingang jeder Methode, ob die Eingangsparameter und der Vorzustand des Zielobjekts stimmen.

Die Nachbedingungen prüfen am Ausgang jeder Methode, ob die Rückgabewerte stimmen und ob der Nachzustand des Zielobjekts immer noch stimmt. Der Klassen-test baut auf einer Interpretation jener Pre- und Post-Konditionen auf, um die ent-sprechenden Testfälle zu generieren. Somit wird die Klasse unter Test schrittweise interpretiert und ihre Zustände mit den zugesicherten Zuständen abgeglichen [MgSy92].

6.4 Theoretische Ansätze zum Klassentest _________________________________169

Thuy schlägt vor, den Klassentest auf die zusätzlichen und erweiterten Methoden zu beschränken. Die geerbten, unveränderten Methoden werden ausgeklammert. Für die Methoden, die getestet werden, gilt es, alle Logikzweige zu durchlaufen und alle Parameter-Kombinationen zu erproben. Zu diesem Zweck sind repräsentative Ob-jekte zu erzeugen, sogar künstliche für die abstrakten Klassen [Thu92].

Hoffman und Strooper haben einen Klassentest auf der Basis eines Testgraphen vorgeschlagen. Ihre Methode heißt CUT für Class under Test. Demnach werden für jede zu testende Klasse drei Testklassen erstellt – eine Testgraphenklasse, eine Treiberklasse und eine Orakelklasse. Die Testgraphenklasse enthält die Testsequen-zen bzw. die Zustandsübergänge der Methoden und deren Abhängigkeiten vonein-ander, einschließlich der Ausnahmebehandlung. Sie wird vom Entwickler verfasst bzw. aus den Zustandsübergangsdiagrammen generiert. Die Treiberklasse holt die Testfolgen aus dem Testgraphen und ruft die Methoden der Klasse unter Test auf.

Sie wird aus der Testgraphenklasse abgeleitet. Die Orakelklasse prüft den aktuellen Zustand der Objekte gegen die in der Testgraphenklasse spezifizierten Zustands-übergänge und bestätigt ihre Korrektheit. Auch sie wird also aus der Testgraphen-klasse abgeleitet. Somit bildet der Testgraph bzw. die Zustandsübergangsdiagram-me den Ausgangspunkt für den Klassentest. Sie sollten deshalb auf der gleichen semantischen Stufe wie die Klasse selbst sein [HoSt93].

Turner und Robson haben ein Testszenario für C++-Objekte entwickelt. Sie weisen darauf hin, dass als Erstes die Konstruktor-Operation aufgerufen werden muss, um eine Objektinstanz anzulegen und als Letztes die Destruktor-Operation aufgerufen werden muss, um die Objektinstanz zu löschen. Dazwischen wird der Zustand des Objekts durch die restlichen Operationen verändert. Prinzipiell ist die Reihenfolge der restlichen Operationsausführungen beliebig. Um die Zwischenzustände der Objekte zu kontrollieren, ist es notwendig, Testszenarien mittels Zustandsüber-gangsdiagrammen zu bestimmen. Jeder Zustandsübergang versetzt das Zielobjekt in einen anderen Zustand, der mit Nachbedingungen vorausgesagt wird. Wenn der vorausgesagte Zustand nicht eintritt, ist dies ein Zeichen dafür, dass die betreffende Operation unkorrekt ist. Die Basis dieser Methode ist die Voraussage einer Reihe Objektzustände, die aufeinander folgen müssen, und die Bestätigung dieser Voraus-sage mittels Zusicherungen [TuRo93].

Kung hat einen Ansatz geprägt, der vom Code ausgeht. Zunächst werden aus den Klassen automatisch Zustandsdiagramme abgeleitet. Diese werden anschließend in einem endlichen Automatengraph zusammengeführt. Daraus geht hervor, welche Zustände überhaupt vorkommen können und welche Bedingungen erfüllt werden müssen, um zu jedem Zustand zu gelangen. Wenn also all jene Bedingungen erfüllt werden, werden auch alle Zustände hervorgerufen. Testfälle werden generiert, um alle Bedingungen zu erzwingen. Dieser Ansatz sichert zwar eine 100%ige Test-überdeckung, testet aber nur die Konsistenz der Bedingungen und Daten [KGH93].

Parrish verfolgt einen datenflussbezogenen Ansatz zum Klassentest. Der Datenfluss beginnt dort, wo eine Operation aufgerufen wird, und endet mit einem Return aus der Klasse. Dazwischen fließen Werte durch die Objektattribute und interne Variab-len. Es ist das Ziel, alle Datenveränderungen aufgrund von SET- und USE-Operationen zu definieren und zu Testfällen zusammenzufassen. Ein Testfall ent-spricht einem Datenflusspfad durch die Methoden. Als Ausgangspunkt empfiehlt Parrish die Returnwerte. Sie sollten durch den Zufluss anderer Werte zurückverfolgt werden [PBC93].

D’Souza und Leblanc haben sich auf das Problem der Objektreferenzen konzent-riert. Sie behaupten, die Hauptfehlerquelle in den Klassen liege in der falschen Referenzierung der Objekte – man meint z.B., man würde die dritte Objektinstanz verarbeiten, wenn man in Wirklichkeit die zweite verarbeitet. Zu verhindern wäre dies über die Suche nach Aliaszeiger in einem Trace. Alle Objektreferenzen und deren Zuweisungen werden registriert und in einem Protokoll festgehalten, welches dem Tester dann zwecks einer visuellen Kontrolle präsentiert wird [DsLe94].

Overbeck hat sich mit dem Test generischer Klassen beschäftigt. Generische Klas-sen sind von zweierlei Art: parametrisierte KlasKlas-sen, die neue KlasKlas-sen schaffen (in C++-Templates) und abstrakte Oberklassen bzw. Operationen, die nur eine Schnitt-stelle ohne die dazugehörige Implementierung spezifizieren (in C++ als virtual

gekennzeichnet, in Java als abstract gekennzeichnete Klassen und Operationen sowie Interfaces). Overbeck behauptet, auch diese Klassen müssen instanziert wer-den, zumindest beim Test. Zu diesem Zweck verwandelt er sie in einfache Klassen mit Konstruktor und Destruktor und ruft sie von einem Testtreiber auf, um ihre Methoden mit den künstlich erzeugten Objekten zu testen. Nur so lässt sich verhin-dern, dass unkorrekte generische Klassen ihre Fehler wie Viren verbreiten [Ove94].

Harrold und Rothermel haben eine Methode für die Modellierung der Interaktionen zwischen Operationen einer Klasse erfunden. Sie behaupten, vielleicht zu Recht, der Schlüssel zum Klassentest liege nicht im Test einzelner Methoden, sondern im Test der Abhängigkeiten zwischen Methoden. Ihre Methode basiert auf drei Arten von Datenfluss: dem Datenfluss innerhalb einer Methode, dem Datenfluss zwischen zwei Methoden und dem Datenfluss über alle Methoden einer Klasse hinweg.

Durch eine statische Analyse des Klassen-Source-Codes werden diese Datenflüsse identifiziert und in einer Treiberklasse als gerichteter Graph abgebildet. Die Trei-berklasse ist sozusagen eine Straßenkarte der Klasse unter Test, wobei die Straßen die Datenflüsse sind. Dieser Testrahmen benutzt DEFINE/USE-Paare, um Testfälle nach dem PLR-Algorithmus zu generieren, um jeden Datenflusspfad bzw. jede Straße durch die Klasse unter Test – CUT – zu „befahren“. Problematisch wird es, wenn verschiedene Variablen das gleiche Objekt refernzieren (aliasing) [HaRo94].

Im Dokument Das Praxishandbuch für den Test (Seite 184-187)