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 31) ebenso hilfreich sein wie für die Integration mit vorhandenen Testmanagement- oder Testdurchführungs-Werkzeugen (vgl. Kapitel 26).

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.

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 53.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
Beispiel 23.9:  Starten des QF-Test Daemon

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
Beispiel 23.10:  Starten des QF-Test Daemon ohne SSL

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+23.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
Beispiel 23.11:  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 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"
Beispiel 23.12:  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 23.13:  Beenden eines QF-Test Daemon

Eine vollständige Übersicht über die calldaemon-Parameter finden Sie im Kapitel Kommandozeilenargumente und Rückgabewerte.

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 53 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)
Beispiel 23.14:  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über hinaus 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 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()
}
Beispiel 23.15:  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-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-7.1.2
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 23.16:  Befehlsskript 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 31), 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 23.17:  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.