Die Ausführung von Tests ist unter verschiedenen Blickwinkeln zu betrachten: Zum einen
werden Tests während der Entwicklung aus der laufenden QF-Test Anwendung heraus ausgeführt,
um die Funktionstüchtigkeit der Tests zu überprüfen. Diese Situation wurde bereits in
Abschnitt 5.2 behandelt; sie reduziert sich im Wesentlichen auf den
Aufruf von »Wiedergabe«-»Start«
im QF-Test Hauptmenü.
Auf der anderen Seite sollen die entwickelten Tests regelmäßig ausgeführt werden, um die
Stabilität der getesteten Software sicherzustellen - etwa bei nächtlichen Regressionstests.
Anstatt die auszuführende Testsuite mit QF-Test zu öffnen und über die Benutzeroberfläche
auszuführen, ist es hier viel praktischer, den Testlauf von der Kommandozeile im
sogenannten Batchmodus zu starten. Diese Art der Testausführung bildet den ersten
Abschnitt dieses Kapitels (Testausführung im Batchmodus).
In manchen Situationen, etwa wenn der Testlauf auf einem entfernten Rechner ausgeführt
werden soll, kommt eine weitere Variante ins Spiel: der Daemonmodus. Hier dient eine
laufende QF-Test Instanz sozusagen als Ausführungsorgan für Tests. Mit diesem Thema wird sich
der zweite Abschnitt befassen (Testausführung im Daemonmodus).
Hinweise zur Integration von QF-Test mit Build Tools wie ant, maven
oder Hudson/Jenkins finden Sie in Kapitel 20.
Grundlage für die Ausführung von Tests über die Kommandozeile bildet eine Vielzahl von
Argumenten, mit denen QF-Test im Batchmodus gestartet werden kann. Anschließend werden
exemplarisch einige davon behandelt; eine Übersicht über alle Optionen findet man in
Kapitel 32.
Die nachfolgenden Beispiele sind für Windows geschrieben, lassen sich aber leicht auf
Linux übertragen. Neben den Pfadangaben unterscheidet sich die Syntax nur noch im Bezug
auf die Platzhalter (Abschnitt 32.2.4): Unter Linux kann neben der Form
+X auch %X verwendet werden. Unter Windows gibt es außerdem neben der GUI-Anwendung
qftest.exe auch eine Konsolen-Variante qftestc.exe. Diese
wartet, bis die Ausführung von QF-Test beendet ist und gibt u. a. auch print-Ausgaben von
einem 'Server Skript' aus.
Der einfachste Aufruf von QF-Test, um einen Test auszuführen, sieht so aus:
|
|
qftest -batch -run c:\mysuites\suiteA.qft |
|
|
| | Beispiel 16.1: Testausführung von der Kommandozeile | |
Das Argument -batch sorgt dafür, dass QF-Test ohne graphische
Benutzeroberfläche gestartet wird. Das zweite Argument, -run, sagt QF-Test,
dass ein Test ausgeführt werden soll. Den Abschluss der Kommandozeile bildet die
auszuführende Testsuite.
Hinweis Das Argument -run ist optional, d. h. die
Testausführung ist als Standard für den Batchmodus definiert.
Führt man die obige Anweisung aus, werden alle 'Test', 'Testfall' und
'Testfallsatz' Knoten nacheinander ausgeführt, die in der Testsuite
suiteA.qft auf der obersten Ebene (d. h. direkt unterhalb des
'Testsuite' Knotens) definiert sind. Nach Beendigung des Testlaufs findet man im
aktuellen Verzeichnis, unter dem gleichen Namen wie die Testsuite, eine
Protokolldatei, der man das Testergebnis entnehmen kann.
Durch Angabe von -nolog kann die Erstellung der Protokolldatei auch
unterdrückt werden. Das macht aber wahrscheinlich nur dann Sinn, wenn man seinen Test
um eigene Protokollausgaben bereichert hat, die etwa in eine Datei geschrieben werden.
Ansonsten lieferte nur noch der Rückgabewert von QF-Test einen Hinweis auf den Ausgang
des Testlaufs: 0 bedeutet, dass der Test erfolgreich absolviert wurde, ein positiver
Wert hingegen ist die Folge von Warnungen, Fehlern oder gar Exceptions (siehe Abschnitt 32.3).
In der Regel möchte man also wohl ein Protokoll des Testlaufs erstellen und dieses an
einer bestimmten Stelle im Dateisystem ablegen. Das geht über den Parameter
-runlog:
|
|
qftest -batch -compact -runlog c:\mylogs\+b c:\mysuites\suiteA.qft |
|
|
| | Beispiel 16.2: Testausführung mit Protokolldatei | |
Nun wird eine Protokolldatei suiteA.qrz im angegebenen Verzeichnis
c:\mylogs erstellt. Der Platzhalter +b sorgt dafür, dass sie
den Namen von der Testsuite übernimmt. Durch die Angabe von -compact wird
erreicht, dass nur die wichtigsten Knoten in die Protokolldatei übernommen werden,
also nur solche, die für den Report und gegebenenfalls die Fehleranalyse benötigt
werden. Insbesondere bei sehr umfangreichen Tests kann dieser Parameter helfen, den
Anspruch an Arbeitsspeicher in Grenzen zu halten. Mittlerweile bietet QF-Test allerdings,
ebenfalls um den Speicherverbrauch zu reduzieren, auch die Möglichkeit,
Protokolldateien zu splitten. Nähere Informationen dazu findet man in
Abschnitt 9.1.
Hinweis Ob die Datei hier tatsächlich als komprimiertes Protokoll
(das ist vom obigen "compact" zu unterscheiden) mit der Endung .qrz
erstellt wird, hängt von den Einstellungen ab. Um ein bestimmtes Format zu erzwingen,
kann die Endung explizit angegeben werden, also z. B. -runlog
c:\mylogs\+b.qrl, wenn unkomprimiertes XML gewünscht wird.
Manchmal möchte man vielleicht nicht die komplette Testsuite, sondern nur bestimmte
Testfälle ausführen. Über den Parameter -test lässt sich ein ganz
bestimmter Knoten zur Ausführung festlegen:
|
|
qftest -batch -runlog c:\mylogs\+b -test "Mein Test" c:\mysuites\suiteA.qft |
|
|
| | Beispiel 16.3: Ausführung eines bestimmten Knotens | |
Die Angabe "Mein Test" bezieht sich dabei auf das Attribut 'Id' des
auszuführenden Knotens; bei 'Testfall' oder 'Testfallsatz' Knoten kann auch deren
qualifizierter Name angegeben werden. Sollen mehrere Knoten ausgeführt werden, kann
das Argument -test <Id> auch mehrfach angegeben werden. Neben einer
Knoten-'Id' versteht -test auch die Angabe eines numerischen Index. So
würde etwa -test 0 den ersten Knoten unterhalb von 'Testsuite'
ausführen.
Das Protokoll liefert eine eher technische Sicht auf den Ablauf eines Tests; es ist
vor allem nützlich, um während eines Testlaufs aufgetretene Fehler zu analysieren
(vgl. Abschnitt 9.1). Eine übersichtliche Darstellung über die
ausgeführten Testfälle und eventuell aufgetretene Fehler bietet hingegen der Report.
Er wird aus der Protokolldatei erstellt und liegt anschließend im XML- und/oder
HTML-Format vor. Der Report kann also auch nachträglich aus einer (oder mehreren)
Protokolldateien erstellt werden (vgl. Kapitel 15). Um gleich bei
der Testausführung dafür zu sorgen, verwendet man -report:
|
|
qftest -batch -runlog c:\mylogs\+b -report c:\mylogs\rep_+b_+y+M+d+h+m c:\mysuites\suiteA.qft |
|
|
| | Beispiel 16.4: Testausführung mit Reportgenerierung | |
Hier werden die XML- und HTML-Reportdateien in einem Verzeichnis erstellt, dessen Name
sowohl die Testsuite wie auch Datum und Uhrzeit enthält, etwa:
c:\mylogs\rep_suiteA_0806042152. Ersetzt man das Argument
-report durch -report.xml bzw. -report.html, so
wird nur der XML- bzw. nur der HTML-Report erstellt.
Testfälle sind oft parametrisiert, das heißt, sie verwenden Variablen, deren Werte den
Testablauf bestimmen. Ist beispielsweise im 'Testsuite' Knoten eine Variable
myvar definiert, kann deren Vorgabewert zur Testausführung überschrieben
werden:
|
|
qftest -batch -variable myvar="Value from command line" -runlog c:\mylogs\+b c:\mysuites\suiteA.qft |
|
|
| | Beispiel 16.5: Testausführung mit Variablen | |
Sollen mehrere Variablen gesetzt werden, kann -variable
<name>=<wert> auch mehrfach angegeben werden.
Die Möglichkeit, Tests über die Kommandozeile ausführen zu können, bildet die
Grundlage zur einfachen Integration von QF-Test in bestehende Testmanagement-Systeme
(siehe Anbindung an Testmanagementtools). Wer hingegen kein Testmanagement-System
betreibt, mag es vielleicht praktisch finden, das Kommando zur Testausführung in ein
Skript einzubetten. Ein einfaches Windows Befehlsskript (qfbatch.bat)
könnte zum Beispiel wie folgt aussehen:
|
|
@echo off
setlocal
if "%1" == "" (
echo Usage: qfbatch Testsuite
goto end
) else (
set suite=%~f1
)
set logdir=c:\mylogs
pushd c:\programs\qftest\qftest-3.4.7\bin
@echo on
.\qftest -batch -compact -runlog %logdir%\+b %suite%
@echo off
if %errorlevel% equ 0 (
echo Test terminated successfully
goto end
)
if %errorlevel% equ 1 (
echo Test terminated with warnings
goto end
)
if %errorlevel% equ 2 (
echo Test terminated with errors
goto end
)
if %errorlevel% equ 3 (
echo Test terminated with exceptions
goto end
)
if %errorlevel% leq -1 (
echo Error %errorlevel%
goto end
)
:end
popd |
|
|
| | Beispiel 16.6: Befehlsskript qfbatch.bat zur Ausführung einer Testsuite | |
Nun braucht man nur noch das Skript auszuführen, mit dem Dateinamen der auszuführenden
Testsuite als Parameter. Der Rest geht von alleine: Die Testsuite wird ausgeführt, die
Protokolldatei im Verzeichnis logdir abgelegt und abschließend gibt das
Skript in Abhänigigkeit vom QF-Test Rückgabewert eine Statusmeldung aus.
Seit Version 3 ist die Sprache Groovy Bestandteil von QF-Test (siehe Kapitel 13). Hauptsächlich gedacht ist sie zum Schreiben von Server- und
SUT-Skripten, aber Groovy kann ebenso wie Jython auch außerhalb von QF-Test verwendet werden.
Diese Sprache ist sicher gut geeignet, um sich selbst ein kleines Management-System
zur Testautomatisierung zu erstellen. Mit Groovy lässt sich übrigens auch der Einsatz
von Ant vereinfachen, denn statt mit klobigen XML-Dateien, über die sich zudem etwa
Bedingungen nur schwer umsetzen lassen, kann man dank AntBuilder mit
übersichtlichem Groovy-Code arbeiten. Das folgende Beispiel kommt allerdings ohne Ant
aus:
|
|
def suite = ''
if (args.size() == 0) {
println 'Usage: groovy QfExec Testsuite'
return
}
else {
suite = args[0]
}
def qftestdir = 'c:\\programs\\qfs\\qftest\\qftest-3.4.7'
def qftest = qftestdir + '\\bin\\qftest.exe'
def command = "$qftest -batch -compact -runlog c:\\mylogs\\+b \"$suite\""
def printStream = { stream ->
while (true) {
try {
stream.eachLine { println it }
} catch (IOException) {
break
}
}
}
println "Running command: $command"
def proc = command.execute()
new Thread().start() { printStream(proc.in) }
new Thread().start() { printStream(proc.err) }
proc.waitFor()
switch (proc.exitValue()) {
case '0': println 'Test terminated successfully'; break
case '1': println 'Test terminated with warnings'; break
case '2': println 'Test terminated with errors'; break
case '3': println 'Test terminated with exceptions'; break
default: println "Error ${proc.exitValue()}"
} |
|
|
| | Beispiel 16.7: Groovy Skript QfExec.groovy zur Ausführung einer Testsuite | |
Sollte Groovy unabhängig von QF-Test auf Ihrem Rechner installiert sein, kann die
Beispiel-Testsuite einfach mit groovy QfExec c:\mysuites\suiteA.qft
ausgeführt werden. Andernfalls kann auch die Groovy jar-Datei aus der QF-Test
Installation verwendet werden. Am besten nutzt man in diesem Fall zur Ausführung von
Groovy wieder ein Befehlsskript:
|
|
@echo off
setlocal
if "%1" == "" (
echo Usage: qfexec Testsuite
goto end
)
set qftestdir=c:\programs\qftest\qftest-3.4.7
set scriptfile=QfExec.groovy
java -cp %qftestdir%/lib/groovy-all.jar groovy.ui.GroovyMain %scriptfile% %*
:end |
|
|
| | Beispiel 16.8: Befehlsskript qfexec.bat zur Ausführung von Groovy (hier
QfExec.groovy) | |
Die Testsuite kann nun mit qfexec c:\mysuites\suiteA.qft ausgeführt
werden.
Im Daemon Modus gestartet lauscht QF-Test auf RMI Verbindungen und stellt darüber ein
Interface für die verteilte Ausführung von Tests zur Verfügung. Dies kann für die
Testdurchführung in einem verteilten Lasttest Szenario (vgl. Kapitel 21)
ebenso hilfreich sein wie für die Integration mit vorhandenen Testmanagement- oder
Testdurchführungs-Werkzeugen (vgl. Kapitel 19).
!!! Warnung !!! Im Daemon Modus akzeptiert QF-Test Verbindungen von
jedem, der Netzwerk-Zugang zum Rechner und dem entsprechenden Port hat. Es findet keine
Überprüfung von Name oder Zugriffsrechten statt. Verwenden Sie dieses Feature
nicht außerhalb einer sicheren Umgebung.
Um mit dem Daemon arbeiten zu können, muss er zunächst auf irgendeinem Rechner im
Netzwerk gestartet werden (das kann natürlich auch localhost sein):
|
|
qftest -batch -daemon -daemonport 12345 |
|
|
| | Beispiel 16.9: Starten des QF-Test Daemon | |
Lässt man das Argument -daemonport weg, lauscht der Daemon auf Port 3543.
Ob der Daemon erfolgreich gestartet wurde, kann man z. B. mit dem Programm
netstat prüfen:
Windowsnetstat -a -p tcp -n | findstr "12345"
Linuxnetstat -a --tcp --numeric-ports | grep 12345
Will man den Daemon auf einem entfernten Rechner starten, bieten sich zum Beispiel ssh
oder VNC an. Ob und wie das ggf. in Ihrem Netzwerk funktioniert, weiß der
Netzwerkadministrator. Um die folgenden Beispiele nachzuvollziehen, reicht aber auch
ein lokaler Dämon.
Die einfachste Möglichkeit, den lauschenden Daemon anzusprechen bietet die
Kommandozeile, indem man QF-Test im sogenannten calldaemon Modus startet. Das
folgende Beispiel prüft, ob der Daemon an der angegebenen Host-/Portkombination
erreichbar ist:
|
|
qftestc -batch -calldaemon -daemonhost localhost -daemonport 12345 -ping && echo Daemon is running |
|
|
| | Beispiel 16.10: Pingen eines QF-Test Daemon | |
Anders als das obige netstat Kommando funktioniert -ping
auch über Rechnergrenzen hinweg (auf dem lokalen Rechner kann man das Argument
-daemonhost einfach weglassen).
Auf ähnliche Weise wie man eine Testsuite im Batchmodus ausführt, kann man nun einen Dämon
dazu bringen, einen bestimmten Testfall auszuführen und ein Protokoll des Testlaufs
zu schreiben:
|
|
qftest -batch -calldaemon -daemonhost somehost -daemonport 12345 -runlog c:\mylogs\+b -suitedir c:\mysuites suiteA.qft#"Mein Testfall" |
|
|
| | Beispiel 16.11: Ausführung eines Testfalls mit dem QF-Test Daemon | |
Hinweis Anders als im Batchmodus wird beim Verwendung eines Daemons ein
'Testfall' oder ein 'Testfallsatz' (andere Knotentypen sind nicht erlaubt) stets über
seinen qualifizierten Namen angesprochen, z.B. "Mein Testfallsatz.Mein Testfall" (zur
Erinnerung: Beim Batchmodus wird -test <Id> verwendet). Will man
die komplette Testsuite suiteA.qft ausführen, so lässt man die Angabe des
Testfalls einfach weg oder schreibt suiteA.qft#..
Wird der Daemon auf einem entfernen Rechner gestartet, gibt man diesen bei der
Ausführung von calldaemon über den Parameter -daemonhost
explizit an (Vorgabe ist -daemonhost localhost). Man beachte, dass sich
dabei der Parameter -suitedir auf den entfernten Rechner bezieht (auf dem
der Daemon läuft), während -runlog eine lokale Datei bezeichnet.
3.4+
Gerade dann, wenn man die Testausführung nicht so leicht beobachten kann, bietet es
sich an, zusätzlich das Argument -verbose anzugeben, um so
Statusinformationen auf der Konsole angezeigt zu bekommen (auf Windows muss dazu
qftestc verwendet werden).
Ein Daemon, lokal oder entfernt, lässt sich über das calldaemon Kommando
-terminate wieder beenden:
|
|
qftest -batch -calldaemon -daemonport 12345 -daemonhost localhost -terminate |
|
|
| | Beispiel 16.12: Beenden eines QF-Test Daemon | |
Eine vollständige Übersicht über die calldaemon-Parameter finden Sie im Kapitel Kommandozeilenargumente und Rückgabewerte.
Das Ansprechen des Daemons über die QF-Test Kommandozeile ist auf der einen Seite ganz
praktisch, auf der anderen jedoch bietet sie nur eingeschränkte Möglichkeiten. Um die
daemonischen Fähigkeiten voll auszureizen, muss man sich der Daemon-API
bedienen. Wir werden diese hier beispielhaft vorstellen, die vollständige
Schnittstelle ist in Kapitel 40 beschrieben.
Für erste Experimente mit der Daemon-API bietet sich ein 'Server Skript' Knoten
an:
|
|
from de.qfs.apps.qftest.daemon import DaemonRunContext
from de.qfs.apps.qftest.daemon import DaemonLocator
host = "localhost"
port = 12345
# Leading r means raw string to allow normal backslashes in the path string.
testcase = r"c:\mysuites\suiteA.qft#Mein Testfall"
timeout = 60 * 1000
def calldaemon(host, port, testcase, timeout=0):
daemon = DaemonLocator.instance().locateDaemon(host, port)
trd = daemon.createTestRunDaemon()
context = trd.createContext()
context.runTest(testcase)
context.waitForRunState(DaemonRunContext.STATE_FINISHED, timeout)
result = context.getResult()
log = context.getRunLog()
rc.addDaemonLog(log)
context.release()
return result
result = calldaemon(host, port, testcase, timeout)
rc.logMessage("Result from daemon: %d" %result) |
|
|
| | Beispiel 16.13: Daemon-API im 'Server Skript' | |
Das Skript zeigt den grundlegenden Mechanismus der Daemon-Ansteuerung:
-
Zunächst muss mit
locateDaemon ein laufender Daemon gefunden werden.
-
Über den Aufruf von
createTestRunDaemon wird eine Umgebung für Testläufe
bereitgestellt.
-
Zur eigentlichen Testausführung benötigt man ein Context-Objekt
(
createContext). Hierzu wird eine (Runtime-)Lizenz benötigt.
-
Über den Context lässt sich der Testlauf starten (
runTest) und dessen
Zustand abfragen. waitForRunState wartet während der (in Millisekunden)
angegebenen Zeitspanne, bis ein bestimmter Zustand eingetreten ist; hier wird eine
Minute lang darauf gewartet, dass der Testlauf abschließt.
-
Schließlich, nach Ende des Testlaufs, liefert der Context über die Methode
getResult einen Rückgabewert, der Auskunft über das Ergebnis des
Testlaufs gibt (vgl. Rückgabewerte von QF-Test).
-
Darüberhinaus kann man über den Context auch das Protokoll des Testlaufs abholen und
mittels der
rc-Methode addDaemonLog in das lokale
Protokoll einfügen.
Hinweis Das Beispielskript verzichtet aus Gründen der
Übersichtlichkeit auf jegliche Fehlerbehandlung. Gerade beim Arbeiten mit einem
Daemon sollte man aber jeden Aufruf auf eventuelle Fehler überprüfen.
Hinweis Ein Nachteil ist mit der Daemon-Steuerung aus einem
'Server Skript' verbunden: Es wird eine zusätzliche QF-Test Lizenz benötigt,
um den Skript-Knoten interaktiv oder im Batchmodus auszuführen. Das gilt
allerdings nicht, wenn man den oben beschriebenen calldaemon Modus verwendet
oder sich außerhalb von QF-Test mit dem Daemon verbindet (siehe unten).
Die Verwendung der Daemon-API ist nicht auf 'Server Skripte' beschränkt.
Außerhalb von QF-Test kann der Daemon über ein Java-Programm oder, einfacher noch, ein
Groovy-Skript angesprochen werden. Das folgende Groovy-Beispiel arbeitet mit mehreren
Daemon-Instanzen und kann daher auch als Ausgangspunkt für Lasttests dienen. Nehmen
wir an, dass auf verschiedenen Rechnern jeweils ein QF-Test Daemon gestartet wurde. Jeder
der Daemonen soll einen bestimmten Testfall ausführen und für jeden der Testläufe soll ein
Protokoll abgelegt werden (daemon1.qrl, ..., daemonN.qrl). Die Testsuite
mit dem auszuführenden Testfall sei allen Daemon-Instanzen über ein Netzlaufwerk (hier
z:) zugänglich.
|
|
import de.qfs.apps.qftest.daemon.DaemonLocator
import de.qfs.apps.qftest.daemon.DaemonRunContext
def testcase = "z:\\mysuites\\suiteA.qft#Mein Testfall"
def logfile = "c:\\mylogs\\daemon"
def timeout = 120 * 1000
def daemons = DaemonLocator.instance().locateDaemons(10000)
def contexts = []
// Start tests
for (daemon in daemons) {
def trd = daemon.createTestRunDaemon()
trd.setGlobal('machines', daemons.size().toString())
def context = trd.createContext()
contexts << context
context.runTest(testcase)
}
// Wait for tests to terminate
for (i in 0..<contexts.size()) {
def context = contexts[i]
context.waitForRunState(DaemonRunContext.STATE_FINISHED, timeout)
byte[] runlog = context.getRunLog()
def fos = new FileOutputStream("$logfile${i + 1}.qrl")
fos.write(runlog)
fos.close()
context.release()
}
|
|
|
| | Beispiel 16.14: Groovy-Daemon-Skript CallDaemon.groovy | |
Zur Ausführung des Groovy-Skripts werden die QF-Test-Bibliotheken qftest.jar, qfshared.jar und
qflib.jar benötigt und außerdem die Groovy-Bibiliothek, die auch
Bestandteil der QF-Test Installation ist. Das folgende Befehlsskript zeigt, wie das geht:
|
|
@echo off
setlocal
set qftestdir=c:\programs\qftest\qftest-3.4.7
set qflibdir=%qftestdir%\qflib
set classpath=%qftestdir%\lib\groovy-all.jar
set classpath=%classpath%;%qflibdir%\qftest.jar;%qflibdir%\qfshared.jar;%qflibdir%\qflib.jar
java -cp %classpath% groovy.ui.GroovyMain CallDaemon |
|
|
| | Beispiel 16.15: Befehlsskript calldaemon.bat zur Ausführung von
Calldaemon.groovy | |
Damit aus dem Daemon-Beispiel ein Lasttest wird (vgl. Kapitel 21),
müssen die Testläufe an mindestens einer Stelle innerhalb von "Mein Testfall"
synchronisiert werden (z. B. nach dem Starten des SUT). Dazu dient die
rc-Methode syncThreads:
|
|
def machines = rc.getNum('machines')
rc.syncThreads('startup', 60000, -1, machines) |
|
|
| | Beispiel 16.16: Groovy 'Server Skript' Knoten zur Synchronisation der Testläufe | |
Die Variable machines bezeichnet die Anzahl der Rechner. Sie wird zum
Beispiel im 'Testsuite' Knoten mit einem Vorgabewert von 1 definiert.
Bei der Ausführung der Testläufe wird sie mit dem aktuellen Wert überschrieben.