25.2 Testausführung im Daemonmodus
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 33) ebenso hilfreich sein wie für die Integration mit vorhandenen Testmanagement- oder Testdurchführungs-Werkzeugen (vgl. Kapitel 28).
HinweisGUI Tests benötigen eine aktive Benutzersession um korrekt ausgeführt werden zu können. Sie finden im Kapitel Aufsetzen von Testsystemen nützliche Tipps und Tricks für die Einrichtung Ihrer Testsysteme. Der technische Hintergrund ist in FAQ 14 beschrieben.
25.2.1 Starten des Daemons
!!! Warnung !!!
Jeder, der Zugriff auf den QF-Test Daemon hat, kann auf dessen Rechner Programme mit den Rechten des Benutzerkontos starten, unter dem der Daemon läuft. Daher sollte Zugriff nur berechtigten Nutzern gewährt werden.
Wenn Sie den Daemon nicht in einer sicheren Umgebung betreiben, in der jeder Nutzer als berechtigt gilt, oder wenn Sie eine eigene Bibliothek zum Zugriff auf den Daemon entwickeln, sollten Sie unbedingt Abschnitt 55.3 lesen. Darin ist beschrieben, wie Sie die Kommunikation mit dem Daemon mittels SSL absichern können.
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
Hinweis Wichtiger Hinweise zur Kompatibilität:
3.5+
Beginnend mit QF-Test Version 3.5 wird für die Kommunikation mit dem Daemon standardmäßig
SSL verwendet. Um mit einer QF-Test Version älter als 3.5 interagieren zu können, muss der
Daemon mit leerem Kommandozeilenargument -keystore <Keystore-Datei> in folgender Form gestartet
werden:
qftest -batch -keystore= -daemon -daemonport 12345
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 Daemon.
3.0+25.2.2 Steuern des Daemons über die QF-Test Kommandozeile
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
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 Daemon 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"
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
Eine vollständige Übersicht über die calldaemon-Parameter finden Sie im Kapitel Kommandozeilenargumente und Rückgabewerte.
25.2.3 Steuern des Daemons über die Daemon API
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 55 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)
if not context.waitForRunState(DaemonRunContext.STATE_FINISHED, timeout):
# Run did not finish, terminate it
context.stopRun()
if not context.waitForRunState(DaemonRunContext.STATE_FINISHED, 5000):
# Context is deadlocked
raise UserException("No reply from daemon RunContext.")
rc.logError("Daemon call did not terminate and had to be stopped.")
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)
Das Skript zeigt den grundlegenden Mechanismus der Daemon-Ansteuerung:
-
Zunächst muss mit
locateDaemonein laufender Daemon gefunden werden. -
Über den Aufruf von
createTestRunDaemonwird 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.waitForRunStatewartet 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
getResulteinen Rückgabewert, der Auskunft über das Ergebnis des Testlaufs gibt (vgl. Rückgabewerte von QF-Test). -
Darüber hinaus kann man über den Context auch das Protokoll des Testlaufs abholen und
mittels der
rc-MethodeaddDaemonLogin 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 keystore = "z:\\mysuites\\mydaemon.keystore"
def password = "strengGeheim"
def locator = DaemonLocator.instance()
locator.setKeystore(keystore)
locator.setKeystorePassword(password)
def daemons = locator.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()
}
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-Bibliothek, die auch
Bestandteil der QF-Test Installation ist. Das folgende Befehlsskript zeigt, wie das geht:
@echo off setlocal set qftestdir=c:\programs\qftest\qftest-9.0.6 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
calldaemon.bat zur Ausführung von
Calldaemon.groovy
Der DaemonLocator kann beim externen Zugriff den Keystore zur Sicherung der Kommunikation
nur automatisch ermitteln, wenn die Datei qftest.jar (wie in diesem Befehlsskript) direkt aus
dem QF-Test Verzeichnis geladen wird. Alternativ kann der Keystore wie im Groovy-Skript gezeigt mit
setKeystore und setKeystorePassword direkt gesetzt werden, oder
indirekt über die System-Properties javax.net.ssl.keyStore und javax.net.ssl.keyStorePassword.
Damit aus dem Daemon-Beispiel ein Lasttest wird (vgl. Kapitel 33),
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)
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.