| Version 3.4.7 |
| Abhängigkeiten |
'Abhängigkeiten' sind ein sehr fortgeschrittenes Feature und leider auch etwas komplex. Sie benötigen ein gutes Grundverständnis der wichtigsten Konzepte von QF-Test wie Kontrollfluss, Definition von Variablen und Fehlerbehandlung, bevor Sie beginnen 'Abhängigkeiten' einzusetzen. Dafür erscheinen gut implementierte 'Abhängigkeiten' fast wie Magie, wenn verschiedene, aus dem Zusammenhang gerissene 'Testfälle' ausgeführt werden und alle Vorbereitungen und Aufräumarbeiten automatisch erledigt werden. Des Weiteren sind 'Abhängigkeiten' unverzichtbar zur Ausführung von Tests im QF-Test Daemon Modus wie in Kapitel 40 beschrieben.
Zum besseren Verständnis der folgenden Abschnitte stellen wir im Verzeichnis
doc/tutorial eine Testsuite namens
dependencies.qft und im Verzeichnis demo/carconfig
eine Testsuite
carconfig_de.qft bereit. Die erste illustriert verschiedene Konzepte von
'Abhängigkeiten', letztere ist ein realistisches Beispiel. Auch die SWT Demo Suite namens
swt_addressbook.qft und das Datentreiber-Demo datadriver.qft
verwenden 'Abhängigkeiten'. Wenn Sie im Debugger in Einzelschritten durch diese Testsuiten
gehen und sich die zugehörigen Variablen Bindungen und Protokolle ansehen, sollten Sie ein
gutes Gefühl für diese Technik bekommen.
| Grundlagen von 'Abhängigkeiten' |
Eine 'Abhängigkeit' definiert einen Satz von Vorbedingungen, die erfüllt sein müssen, damit ein Test durchgeführt werden kann. Derartige Vorbedingungen hängen oft voneinander ab. So kann es zum Beispiel notwendig sein, zunächst externe Daten einzulesen, bevor das SUT gestartet wird, was wiederum eine Voraussetzung dafür ist, dass das Zielfenster für einen Testfall geöffnet werden kann, etc.
'Abhängigkeiten' können an zwei Stellen definiert werden: Allgemeine 'Abhängigkeiten', die oft wiederverwendet werden und als Grundlage für weitere 'Abhängigkeiten' dienen, können genau wie ein 'Prozedur' Knoten implementiert und unterhalb des 'Prozeduren' Knotens platziert werden, zum Beispiel in einem 'Package' Knoten namens "Dependencies". Der qualifizierte Name wird genau wie der einer 'Prozedur' gebildet und 'Abhängigkeiten' können analog zu einem 'Prozeduraufruf' durch einen 'Bezug auf Abhängigkeit' Knoten referenziert werden.
Alternativ können 'Abhängigkeiten' am Beginn eines 'Testsuite', 'Testfallsatz' oder 'Testfall' Knotens implementiert werden. Zusätzlich zu ihrer eigenen 'Abhängigkeit' können 'Testfälle' und 'Testfallsätze' die 'Abhängigkeit' von ihrem Parentknoten erben.
Eine 'Abhängigkeit' sollte sich jeweils nur um eine Vorbedingung kümmern. Hat diese ihrerseits Vorbedingungen, sollten diese zu Grunde liegenden Schritte von anderen 'Abhängigkeiten' übernommen werden. Dies kann implizit durch Erben der 'Abhängigkeit' von einem Parentknoten oder explizit durch einen 'Bezug auf Abhängigkeit' Knoten geschehen.
Die eigentliche Implementation der Vor- und Nachbedingungen geschieht in 'Vorbereitung' und 'Aufräumen' Knoten innerhalb der 'Abhängigkeit'. Der 'Vorbereitung' Knoten sollte immer so implementiert werden, dass er die Vorbedingung mit dem geringst möglichen Aufwand erfüllt. Ein 'Vorbereitung' Knoten zum Starten des SUT sollte also zunächst prüfen, ob das SUT bereits läuft und nur bei Bedarf die Startsequenz für das SUT ausführen. Auf diesen Punkt wird weiter unten noch einmal genauer eingegangen. Außerdem sollten 'Vorbereitung' und 'Aufräumen' immer in der Lage sein, Fehler zu ignorieren, die keinen Einfluss auf das Ergebnis haben. So sollte zum Beispiel ein 'Aufräumen' Knoten, der das SUT beendet, nicht zu einer Exception führen, wenn das SUT bereits beendet war.
| Der Stapel von Abhängigkeiten |
Wird ein 'Abhängigkeit' Knoten ausgeführt, linearisiert QF-Test zunächst die 'Abhängigkeit' und alle implizit und explizit einbezogenen 'Abhängigkeiten' in einen Stapel von Abhängigkeiten. Hängt zum Beispiel die 'Abhängigkeit' D von den 'Abhängigkeiten' B und C ab, die ihrerseits auf der 'Abhängigkeit' A beruhen, ergibt das den Stapel [A,B,C,D]. Wenn dies die erste 'Abhängigkeit' ist die ausgeführt wird, also bisher kein Stapel von Abhängigkeiten existiert, führt QF-Test die 'Vorbereitung' Knoten der 'Abhängigkeiten' der Reihe nach aus, dabei natürlich die grundlegenden 'Abhängigkeiten' zuerst, also A-B-C-D.
|
| ![]() |
||
|
| Abbildung 12.1: Stapel von Abhängigkeiten A-B-C-D | ||
Nachdem der 'Testfall', der diese 'Abhängigkeit' benötigt hat ausgeführt wurde, wird eventuell ein Teil des Stapels wieder abgebaut. Falls bei einer der 'Abhängigkeiten' auf dem Stapel das Attribut 'Aufräumen erzwingen' gesetzt ist, werden von dieser und der nachfolgenden 'Abhängigkeit' die 'Aufräumen' Knoten ausgeführt, diesmal in der anderen Reihenfolge. Wenn also im obigen Beispiel die 'Abhängigkeit' C das Attribut 'Aufräumen erzwingen' gesetzt hat, wird zunächst der 'Aufräumen' Knoten von D ausgeführt, dann der von C. Anschließend hat der Stapel von Abhängigkeiten die Form [A,B].
|
| ![]() |
||
|
| Abbildung 12.2: 'Aufräumen erzwingen' bei 'Abhängigkeit' C | ||
Nehmen wir nun an, dass ein weiterer 'Testfall' mit einer 'Abhängigkeit' E ausgeführt wird, die ihrerseits von der 'Abhängigkeit' A abhängt. Der neue Zielstapel von Abhängigkeiten ist damit [A,E]. Diesen vergleicht QF-Test nun mit dem aktuellen Stapel von Abhängigkeiten [A,B] und stellt fest dass die erste Abweichung in diesem Fall bei B respektive E auftritt. Der alte Stapel wird nun von rechts nach links bis zu diesem Punkt hin abgebaut. Im Beispiel wird daher der 'Aufräumen' Knoten von B ausgeführt. Der aktuelle Stapel ist nun [A]. Nun wird die 'Abhängigkeit' E hinzugefügt so dass sich [A,E] ergibt.
|
| ![]() |
||
|
| Abbildung 12.3: Ab- und Aufbau des Stapels zu A-E | ||
Der folgende Punkt ist sehr wichtig: Um vom abgebauten alten Stapel [A] zum neuen Zielstapel [A,E] zu gelangen, führt QF-Test nicht einfach die restlichen 'Vorbereitung' Knoten aus - in diesem Fall also den Knoten von E. Stattdessen werden alle 'Vorbereitung' Knoten des gesamten neuen Zielstapels ausgeführt, also zunächst der 'Vorbereitung' Knoten von A, dann der von E. Diese wiederholte Ausführung ist der Hauptgrund warum 'Vorbereitung' Knoten immer mit minimalem Aufwand implementiert werden sollten.
Warum diese eigenartige Asymmetrie? Nehmen wir an dass die 'Abhängigkeit' von A für das Starten des SUT verantwortlich ist. Im normalen Verlauf des Beispiels wird das SUT also bereits gestartet sein und wenn der 'Vorbereitung' Knoten von A richtig implementiert ist wird er dies feststellen und nichts tun, so dass praktisch kein unnötiger Aufwand entsteht. Da QF-Test aber keine Kontrolle darüber hat, was seit der letzten Ausführung des 'Vorbereitung' Knotens von A passiert ist, kann es ebenso gut sein dass das SUT nicht mehr läuft, zum Beispiel weil Sie es manuell beendet haben oder in Folge eines Seiteneffekts des vorhergehenden Tests. Würde der 'Vorbereitung' Knoten von A nicht ausgeführt, könnte die Vorbedingung von E nicht erfüllt werden und der ganze 'Testfall' würde fehlschlagen. Auch alle weiteren Tests, die direkt oder indirekt auf der 'Abhängigkeit' A basieren, hätten Probleme. Daher ist die Ausführung aller 'Vorbereitung' Knoten auf dem Zielstapel die einzige Möglichkeit sicherzustellen, dass wirklich alle Vorbedingungen erfüllt sind.
Wenn Sie einmal den Überblick verlieren oder stecken bleiben, können Sie den Stapel der Abhängigkeiten auf zwei Arten leeren: Über den Menüeintrag »Wiedergabe«-»Abhängigkeiten auflösen« wird der Stapel "sauber" abgebaut indem alle 'Aufräumen' Knoten von rechts nach links ausgeführt werden. Mittels »Wiedergabe«-»Abhängigkeiten zurücksetzen« wird der Stapel dagegen einfach gelöscht, ohne dass irgendein Knoten ausgeführt wird.
| Eskalation von Fehlern |
Eine weitere hervorragende Eigenschaft von 'Abhängigkeiten' ist die Möglichkeit, Fehler ohne weiteren Aufwand zu eskalieren. Betrachten wir wieder das Beispiel aus dem vorhergehenden Abschnitt, nachdem der erste Stapel von Abhängigkeiten mit [A,B,C,D] aufgebaut und alle zugehörigen 'Vorbereitung' Knoten ausgeführt wurden. Was passiert, wenn bei der Ausführung des eigentlichen 'Testfall' Knotens das SUT auf einen wirklich schweren Fehler stößt, es zum Beispiel in ein Deadlock geht und nicht mehr auf Eingaben reagiert?
Schlägt beim Abbau des Stapels von Abhängigkeiten die Ausführung eines 'Aufräumen' Knotens fehl, baut QF-Test eine weitere 'Abhängigkeit' ab, bei erneuten Problemen eine weitere und so fort. Analog dazu führt beim Aufbau des Stapels ein Fehler in einem 'Vorbereitung' Knoten dazu, dass zunächst eine weitere 'Abhängigkeit' abgebaut wird und dann noch einmal alle 'Vorbereitung' Knoten des gesamten Stapels von vorne ausgeführt werden. Im obigen Beispiel würde das Deadlock so lange zu Fehlern führen, bis im 'Aufräumen' der entsprechenden 'Abhängigkeit' das SUT beendet und damit in der nächsten Runde neu gestartet wird.
|
| ![]() |
||
|
| Abbildung 12.4: Exception beim Aufräumen von C bewirkt Aufräumen von B | ||
Damit dies zuverlässig funktioniert, ist es sehr wichtig, 'Aufräumen' Knoten so zu implementieren, dass entweder der gewünschte Zustand erreicht oder eine Exception geworfen wird und eine grundlegendere 'Abhängigkeit' mit einem umfassenderen 'Aufräumen' Knoten vorhanden ist. Wenn zum Beispiel der 'Aufräumen' Knoten zum Beenden des SUT nur versuchen würde, das SUT etwa über das Datei->Beenden Menü sauber zu beenden, ohne dabei auf Exceptions zu achten oder sicherzustellen, dass das SUT danach auch wirklich beendet ist, könnte dies zu Problemen in nachfolgenden Tests führen. Ein solcher 'Aufräumen' Knoten sollte also einen 'Try'/'Catch' Block verwenden und zusätzlich in einem 'Finally' Knoten sicherstellen, dass das SUT tatsächlich beendet ist und andernfalls den Prozess des SUT terminieren.
Bei sauber implementierter Fehlerbehandlung werden sich 'Testfälle' selbst bei schweren Fehlern kaum gegenseitig beeinflussen. Dies schützt vor dem Verlust aller Testergebnisse eines lang dauernden nächtlichen Testlaufs nur aufgrund eines einzelnen Fehlers zu einem frühen Zeitpunkt.
| Spezielle Variablen |
Eine 'Abhängigkeit' kann ihrerseits von den Werten gewisser Variablen abhängen. Wenn zum Beispiel das SUT mit verschiedenen JDK Versionen getestet werden soll, ist die JDK Version eine charakteristische Variable für die 'Abhängigkeit', die das SUT startet. Ist der aktuelle Stapel von Abhängigkeiten [A] und der Zielstapel ebenfalls [A], würde der Stapel normalerweise nicht abgebaut. Wird aber diesmal eine andere JDK Version für das SUT gewünscht als beim letzten Fall, muss QF-Test die 'Abhängigkeit' auflösen, um das SUT zu beenden, so dass es anschließend mit dem richtigen JDK neu gestartet werden kann.
|
| ![]() |
||
|
| Abbildung 12.5: Änderung in charakteristischer Variable bewirkt Aufräumen von A | ||
All dies wird vollkommen automatisch durchgeführt, wenn Sie den Namen der Variable mit der JDK Version in der 'Abhängigkeit' zum Attribut 'Charakteristische Variablen' hinzufügen. Die Werte der charakteristischen Variablen werden beim Vergleich der Stapel von Abhängigkeiten mit herangezogen. Zwei 'Abhängigkeit' Knoten auf dem jeweiligen Stapel gelten nur dann als gleich, wenn auch die Werte aller 'Charakteristischen Variablen' zwischen der vorherigen und der aktuellen Ausführung übereinstimmen. Somit ist es auch möglich, dass eine 'Abhängigkeit' sich, direkt oder indirekt, mehrfach auf die selbe 'Abhängigkeit' bezieht, aber mit unterschiedlichen Werten für die 'Charakteristischen Variablen'. In diesem Fall taucht die bezogene 'Abhängigkeit' mehrfach im linearisierten Stapel auf.
Außerdem speichert QF-Test bei der Ausführung des 'Vorbereitung' Knotens einer 'Abhängigkeit' die Werte der 'Charakteristischen Variablen'. Wird die 'Abhängigkeit' abgebaut und ihr 'Aufräumen' Knoten ausgeführt, werden diese Variablen wieder an die Werte vom Zeitpunkt der Ausführung des 'Vorbereitung' Knotens gebunden. Dadurch wird sicher gestellt, dass die Ausführung eines 'Testfall' Knotens mit völlig anderen Werten die 'Aufräumen' Knoten beim Abbau des Stapels von Abhängigkeiten nicht aus der Bahn werfen kann. Denken Sie zum Beispiel an die häufig für den Namen des SUT Clients verwendete Variable "client". Wird zunächst ein Satz von Tests für ein SUT ausgeführt und benötigt der nächste 'Testfall' ein ganz anderes SUT, wird eventuell die Variable "client" umdefiniert. Der 'Aufräumen' Knoten zum Beenden des alten SUT muss aber den alten Wert für "client" erhalten, sonst könnte er den alten SUT Client gar nicht beenden. Darum kümmert sich QF-Test automatisch, sofern sich "client" unter den 'Charakteristischen Variablen' der 'Abhängigkeit' befindet.
| Fehlerbehandlung |
Neben der automatischen Eskalation von Fehlern unterstützt ein 'Abhängigkeit' Knoten auch
die explizite Behandlung von Fehlern oder Exceptions, die bei der Ausführung eines
'Testfalls' auftreten. Hierzu können 'Catch' Knoten am Ende des
'Abhängigkeit' Knotens eingefügt werden. Diese fangen im 'Testfall' auftretende
Exceptions und können gezielt darauf reagieren. So könnte zum Beispiel eine
DeadlockTimeoutException mit umgehender Terminierung des SUT Prozesses quittiert
werden. Eine derart gefangene Exception wird in Protokoll und Report weiterhin als
Exception aufgeführt. Insgesamt ist der Mechanismus ähnlich zum Attribut 'Implizit Exceptions fangen'
eines 'Test' Knotens, aber wesentlich spezifischer.
Des Weiteren kann ein 'Fehlerbehandlung' Knoten in der 'Abhängigkeit' zwischen dem 'Aufräumen' Knoten und dem ersten 'Catch' Knoten eingefügt werden. Dieser wird ausgeführt, wenn der 'Testfall' mit dem Ergebnis "Fehler" beendet wird. Im Fall einer Exception wird der 'Fehlerbehandlung' Knoten nicht ausgeführt, da das in der Regel eher noch mehr Probleme verursachen würde und sogar mit der Behandlung von Exceptions kollidieren könnte. Um im Fall von Fehlern und Exceptions die selben Konsequenzen zu ziehen, können Sie dafür eine 'Prozedur' implementieren und diese aus 'Fehlerbehandlung' und 'Catch' Knoten heraus aufrufen. 'Fehlerbehandlung' Knoten sind nützlich, um Informationen zu ermitteln und zu speichern, die QF-Test nicht automatisch zur Verfügung stellt. So könnten Sie beispielsweise eine Kopie von temporären Dateien oder Protokollen erstellen, die von Ihrem SUT angelegt werden und eventuell Aufschlüsse über die Fehlerursache liefern könnten.
|
| ![]() |
||
|
| Abbildung 12.6: Ausführung von 'Catch' und 'Fehlerbehandlung' Knoten | ||
Es wird jeweils nur der oberste 'Fehlerbehandlung' Knoten auf dem Stapel von Abhängigkeiten ausgeführt. Wenn also bei einem Stapel der Form [A,B,C,D] sowohl A als auch C einen 'Fehlerbehandlung' Knoten enthalten, wird nur der Knoten von C ausgeführt. Andernfalls wäre es schwierig in der spezialisierten 'Abhängigkeit' C die Fehlerbehandlung der allgemeineren 'Abhängigkeit' A zu modifizieren. Um die Fehlerbehandlung von A in C wiederzuverwenden, implementieren Sie diese in einer 'Prozedur'.
| Letzte Änderung: 23.04.2012 Copyright © 1999-2012 Quality First Software GmbH |