zurück
Avatar of Pascal Bihler
Autor: Pascal Bihler
04. Juli 2019

Neues beim QF-Modul print(…) und println(…)

Wer sich in QF-Test mit Script-Knoten beschäftigt, ganz gleich ob er jetzt Jython, Groovy oder JavaScript bevorzugt, wird früher oder später über das "QF-Modul stolpern. Dieses Modul, welches in allen Script-Knoten und Skript-Terminals unter dem Variablennamen qf zur Verfügung stellt, bündelt eine Reihe von Hilfsmethoden, die einem das Leben als Skript-Programmierer erleichtern.

Ab und zu wird dieses Modul um neue Methoden ergänzt - In QF-Test 4.6.0 kamen die Methoden print und println hinzu. Eine gute Gelegenheit, mal einen Blick auf das Modul mit seinen neuen Methoden zu werfen.

qf.print(ln)

Die Methode qf.print(…) gibt eine Zeichenkette (bzw. den String-Wert eines Objektes) im QF-Test-Terminal (und bei SUT-Skripten auch im Client-Terminal) aus, qf.println(…) hängt noch einen Zeilenumbruch an die Ausgabe, so dass die nächste Ausgabe dann in der nächsten Zeile anfängt. Aber kann das nicht auch ein einfaches print (ohne qf. davor)?

Im Prinzip schon, aber… Das klassische print gibt die Zeichenkette auf der sogenannten Standard-Ausgabe des Programms aus. QF-Test fängt diese ab und leitet die Ausgabe ins Terminal um. Und meistens geht das auch gut, aber insbesondere in SUT-Skripten tritt immer mal wieder das Problem auf, dass das Client-Programm selbst die Standard-Ausgabe "verbiegt" und so die Ausgabe nie bei QF-Test ankommt. Insbesondere beim Resolver-Schreiben ist dies für das klassische "Print-Debugging" sehr ärgerlich.

qf.print(…) umgeht nun dieses Problem, indem es die Zeichenkette nicht über die normalen Ausgabe-Kanäle an QF-Test weiterreicht, sondern "direkt" über die interne RMI-Schnittstelle (also sozusagen über die gleiche "Leitung", über die QF-Test auch das SUT fernsteuert und Werte für Checks und Events überträgt). Damit ist es robust gegen alle client-spezifischen Ausgabe-Anpassungen und zusätzlich meistens auch noch einen Tick effizienter.

Das Kleingedruckte

Auf einige Seiteneffekte möchte ich gerne noch eingehen:

1. qf.print(…) kann nicht nur einen Parameter, sondern beliebig viele aufnehmen, und verbindet diese dann mit einem Leerzeichen. Dieses kleine JavaScript-Programm:

const x = 1
const y = 1
const z = x + y
qf.println(x,y,z)

erzeugt also im Terminal die Ausgabe "1 1 2".

2. qf.print(…)ruft für die Ausgabe die Java-String-Methode toString() auf. Wer also bisher vergeblich versucht hat, zu überprüfen, ob das array-Modul in Jython tatsächlich ein Java-Array erzeugt:

import array
from java.lang import String
 
print array.array(String,("a","b","c")) # array(java.lang.String, [u'a', u'b', u'c'])

der kann dazu nun qf.println(…) verwenden:

import array
from java.lang import String
 
qf.println(array.array(String,("a","b","c"))) # [Ljava.lang.String;@353191ee


Die erste Variante gibt die Jython-String-Repräsentation des Arrays aus ("When an array object is printed or converted to a string, it is represented as array(typecode, initializer). "), die aber noch nicht den Blick von Java auf das Objekt darstellt. Bei der zweiten Variante wird nun die Ausgabe von toString() auf Java-Arrays wiedergegeben, also die Typinformation und sein Hash-Code (Ausnahmsweise ist diese Ausgabe hier einmal hilfreich, um festzustellen, dass es sich wirklich um ein Java-Array handelt. Sonst bevorzugt man ja in der Java-Welt eher Arrays.deepToString(a), um einen Blick auf den Inhalt werfen zu können). Möchte man über qf.print(…) die Jython-String-Repräsentation ausgeben, so hilft ein zusätzliches str(…): qf.println(str(array.array(String,("a","b","c")))).

3. Wie auch die anderen "Standard"-Variablen (rc, out, resolvers, notifications, …) funktioniert bei Groovy qf erst mal nur in der obersten Ebene des Groovy-Skripts:

 

class InnerClass {
    def printSomething() {
        println("Something")
    }
     
    def printSomethingElse() {
        qf.println("SomethingElse")
    }
}
 
def c = new InnerClass()
 
c.printSomething() // => Kommt bei einem Server-Skript nicht im Termial an, da "println" direkt auf die Standard-Ausgabe umleitet.
c.printSomethingElse() // => "No such property: qf for class: InnerClass"

Abhilfe schafft hier, die Bindings über den Konstruktor in die innere Klasse weiterzureichen:

class InnerClass {
    
    def out,qf
     
    def printSomething() {
        out.println("Something")
    }
     
    def printSomethingElse() {
        qf.println("SomethingElse")
    }
}
 
def c = new InnerClass(out:out,qf:qf)
 
c.printSomething()
c.printSomethingElse()

Insbesondere bei der Entwicklung eigener Checker oder beim Ergänzen älterer Resolver ist dies eine einfache wie effiziente Vorgehensweise.

Neuer Kommentar
( wird auf der Seite/Blog erscheinen )
( wird nicht auf der Seite/Blog erscheinen )

0 Kommentare