zurück
Avatar of Yann Spöri
Autor: Yann Spöri
18. August 2021

TestRunListener in QF-Test

Das TestRunListener Interface kann genutzt werden um vor oder nach der Ausführung eines Knotens oder beim Fehlern weitere Aktionen auszuführen.

Diese Aktionen können zum Beispiel der Testdokumentation oder der Fehleranalyse dienen. Im Folgenden werden einige TestRunListeners vorgestellt (Jython Server Skripte):

Variablen überwachen

Folgender TestRunListener loggt immer dann einen Fehler, wenn sich der Wert einer bestimmten Variable (im Bespiel offset) ändert. Dies ist vor allem beim debuggen einer Testsuite hilfreich wenn nicht klar ist wo genau der Variable ein bestimmter Wert zugewiesen wird:

varname = "offset"

from de.qfs.apps.qftest.extensions.qftest import AbstractTestRunListener

def checkVar(state, var):
    try:
        val = rc.lookup(var)
        if(val != state):
            state = val
            rc.logError("Variable '%s': %s" % (var, state))
    except:
        if state != "":
            rc.logError("Variable '%s' nicht verfügbar" % var)
            state = ""
    return state

class VarChanger (AbstractTestRunListener):
    def __init__(self):
        self.state = None
    def nodeEntered(self, event):
        self.state = checkVar(self.state, varname)
    def nodeExited(self, event):
        self.state = checkVar(self.state, varname)

global varChanger

try:
    rc.removeTestRunListener(varChanger)
except:
    pass
varChanger = VarChanger()
rc.addTestRunListener(varChanger)

Alternativ kann dieses Skript leicht so angepasst werden, dass der TestRunListener immer dann anschlägt wenn der Variable ein bestimmter Wert zugewiesen wird.

Fehlerton

Es kann vorkommen, dass ein Benutzer mehrere Tests auf mehreren Computern gleichzeitig Tests anwirft. Folgender TestRunListener hilft, wenn es vorkommen kann, dass die Testausführung auf einer Maschine - aus welchen Gründen auch immer - ins stocken geraten kann; Der TestRunListener spielt nämlich immer dann einen Ton ab, wenn die Ausführung eines Knotens länger als ein bestimmtes vorgegebenes Zeitlimit dauert. Hierbei ist es möglich dieses Zeitlimit für einen Knoten mithilfe eines @informUserTimeOut Doctags neu zu definieren. (Hinweis: Die Tonausgabe funktioniert wohl nur auf Windows).

# A TestRunListener that informs the user when the current testrun has stopped for more then a certain period of time.

# some imports we need later
from de.qfs.apps.qftest.extensions.qftest import AbstractTestRunListener
from java.lang import System, Thread
from java.awt import Toolkit
import time, re

# The default time in ms after which to inform
# the user.
TIME_IN_MS_AFTER_WHICH_TO_INFORM_THE_USER = 2 * 60 * 1000

# The function that is used in order to inform the user
def informUserAction(lastActionTimestamp):
    """ This function is called whenever the test-
        runlistener detected the timeout. In oder words,
        this function should play a sound or do something
        else in order to inform the user.

        :lastActionTimestamp: The timestamp of the last action.
    """
    print "=== HEY %s ms passed, since we entered the last node! ===" % (System.currentTimeMillis() - lastActionTimestamp)
    # output a sound on windows
    Toolkit.getDefaultToolkit().getDesktopProperty("win.sound.exclamation").run()

# the thread that will inform you once the timeout is reached ...
class InformingThread(Thread):
    def __init__(self):
        self.updateTimeout(TIME_IN_MS_AFTER_WHICH_TO_INFORM_THE_USER)
        self.stop = False
    def updateTimeout(self, timeout):
        self.lastAction = System.currentTimeMillis()
        self.errorAfterTimestamp = self.lastAction + timeout
    def run(self):
        while not self.stop:
            time.sleep(1)
            if System.currentTimeMillis() > self.errorAfterTimestamp and not self.stop:
                try: informUserAction(self.lastAction)
                except: pass

# the testrunlistener that keeps track of when a new node get's enetered ...
class InformUserWhenHaveBeenIdleTestRunListener(AbstractTestRunListener):
    def __init__(self):
        self.thread = InformingThread()
        self.thread.start()
        self.myRegex = re.compile("@informUserTimeOut\\s+(\\d+)",  re.DOTALL)
    def nodeEntered(self, event):
        timeout = 0
        comment = event.getNode().comment
        if comment != None and comment.find("@informUserTimeOut") != -1:
            match = self.myRegex.search(comment)
            if match:
                timeout = int(match.group(1))
        self.thread.updateTimeout(TIME_IN_MS_AFTER_WHICH_TO_INFORM_THE_USER if timeout <= 0 else timeout)
    def runStopped(self, event):
        self.thread.stop = True

# register the testrun listener
global informUserWhenHaveBeenIdleTestRunListener
try:
    informUserWhenHaveBeenIdleTestRunListener.thread.stop = True
    rc.removeTestRunListener(informUserWhenHaveBeenIdleTestRunListener)
except:
    pass
informUserWhenHaveBeenIdleTestRunListener = InformUserWhenHaveBeenIdleTestRunListener()
rc.addTestRunListener(informUserWhenHaveBeenIdleTestRunListener)

Check-Knoten zählen

Folgender TestRunListener zählt wie viele Check-Knoten während einer Testausführung von QF-Test ausgeführt wurden:

 

from de.qfs.apps.qftest.extensions.qftest import AbstractTestRunListener

class StatisticTestRunner (AbstractTestRunListener):
    def __init__(self):
        self.steps, self.checkSteps = 0, 0
    def runStopped(self, event):
        print "steps: %s\ncheckSteps: %s" % (self.steps, self.checkSteps)
        self.steps, self.checkSteps = 0, 0 # reset counts
    def nodeEntered(self, event):
        self.steps += 1
        if (event.getNode().getType().startswith("Check")):
           self.checkSteps += 1

global statisticTestRunner

try:
    rc.removeTestRunListener(statisticTestRunner)
except:
    pass
statisticTestRunner = StatisticTestRunner()
rc.addTestRunListener(statisticTestRunner)

    Generelle Hinweise

    • Ähnlich wie in SUT Skripten sollte der Aufruf von rc.callProcedure innerhalb eines TestRunListener vermieden werden. Es ist zwar oft möglich rc.callProcedure innerhalb eines TestRunListener aufzurufen, allerdings kann dies auch zu unvorhergesehenen Problemen führen.
    • Anstelle von TestRunListener sollte immer von AbstractTestRunListener abgeleitet werden.

    Die Kommentarfunktion ist für diesen Artikel deaktiviert.

    0 Kommentare