3.1+54.4
Implementierung eigener Checks mit dem Checker
Interface
Checks gehören zu QF-Test's nützlichsten Features. Ohne die Fähigkeit, die
Ergebnisse von simulierten Aktionen zu verifizieren, wäre Testautomatisierung weitgehend
nutzlos. Allerdings beschränkt sich das Repertoire von Checks in QF-Test natürlich auf die
gängigen Attribute der standard Komponenten. Für besondere, selten genutzte Attribute
oder für eigene Komponenten können Sie auf SUT-Skript Knoten ausweichen,
dort den benötigten Wert auslesen und über die Methode rc.checkEqual()
mit
dem erwarteten Wert vergleichen. So ein SUT-Skript Knoten funktioniert
schnell und zuverlässig und lässt sich in einer Prozedur modularisieren. Er
hat aber zwei Nachteile: Er lässt sich nicht aufzeichnen und er ist abschreckend für
Nicht-Entwickler.
Mit Hilfe der in diesem Abschnitt beschriebenen API ist es möglich, die Standardchecks
von QF-Test zu erweitern. QF-Test's eigene Checks basieren sogar selbst darauf. Durch
Implementieren und Registrieren eines Checkers
für eine Klasse von GUI
Elementen können Sie Ihre eigenen Checks erstellen, die genauso aufgenommen und
wiedergegeben werden können wie die Standardchecks.
Um dies so einfach wie möglich zu gestalten kümmert sich QF-Test selbst um alle Details,
von der Darstellung im Check-Popupmenü, Abholen der Checkdaten, Aufnahme des
entsprechenden Check Knotens um die Daten zu speichern, Schicken der Daten an
das SUT zur Wiedergabe, Abholen der dann gültigen Checkdaten bis hin zum Vergleichen der beiden
Werte und Darstellung der Ergebnisse im Protokoll. Alles was Sie dazu beitragen müssen,
ist QF-Test mitzuteilen, welche Checks Ihr Checker
implementiert und für diese
auf Anfrage die Checkdaten zu ermitteln.
Illustrative Beispiele finden Sie am Ende des Kapitels sowie in der Testsuite
carconfigSwing_de.qft
im Verzeichnis demo/carconfigSwing
Ihrer QF-Test
Installation.
Das Checker
Interface
Das Interface de.qfs.apps.qftest.extensions.checks.Checker
muss
implementiert werden, um eigene Checks für Ihre Anwendung bereitzustellen. Die damit
verbundenen Hilfsklassen und Interfaces werden in den folgenden Abschnitten
beschrieben.
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
Die Pair
Klasse
Die Klasse de.qfs.lib.util.Pair
für den Rückgabewert von
getCheckDataAndItem
ist eine einfache Hilfsklasse, die praktisch für die
Gruppierung von zwei Objekten ist. Sie müssen nur ihren Konstruktor kennen, können
aber natürlich auch ihre Werte auslesen:
|
|
||||||||||||||||||||||||||||||
Das CheckType
Interface und seine Implementierung
DefaultCheckType
Ein de.qfs.apps.qftest.extensions.checks.CheckType
kapselt die Definition
einer spezifischen Art von Check. Er kombiniert einen CheckDataType
mit
einer Bezeichnung und stellt eine anwenderfreundliche Repräsentation für das Checkmenü
bereit. Sofern Sie nicht mehrsprachige Darstellungen im Checkmenü benötigen, sollten
Sie dieses Interface nie selbst implementieren, sondern einfach einen
de.qfs.apps.qftest.extensions.checks.DefaultCheckType
instanziieren:
|
|
||||||||||||||||
Der Vollständigkeit halber führen wir auch die Methoden des CheckType
Interfaces auf:
|
|
||||||||||||||||||||||||||
Die Klasse CheckDataType
Die Klasse de.qfs.apps.qftest.extensions.checks.CheckDataType
ist
vergleichbar zu Enum
. Sie definiert einige Konstanten von
CheckDataType
Instanzen, die dazu dienen, die Art der Daten zu
definieren, auf denen ein Check beruht. Jede Konstante entspricht dabei einem der
verfügbaren 'Check'-Knoten von QF-Test.
Außer seiner Funktion als Identifikator hat ein CheckDataType
keine
public Attribute oder Methoden und Sie können keine neuen CheckDataType
Objekte erstellen. Wenn Sie einen Check implementieren wollen, der nicht zu den
verfügbaren Datentypen passt, müssen Sie Ihre Daten entsprechend konvertieren, z.B. in
einen String. Folgende CheckDataType
Konstanten sind definiert:
- STRING
- Ein einzelner String. Entspricht dem Check Text Knoten.
- STRING_LIST
- Eine Liste von Strings, wie die Zellen einer Tabellenspalte. Entspricht dem Check Elemente Knoten.
- SELECTABLE_STRING_LIST
- Eine Liste von selektierbaren Strings, wie die Elemente einer Liste. Entspricht dem Check selektierbare Elemente Knoten.
- BOOLEAN
- Ein Boolean Zustand, entweder true oder false. Entspricht dem Check Boolean Knoten.
- GEOMETRY
- Ein Satz von 4 Integer Werten für X und Y-Koordinaten, Breite und Höhe. Nicht alle müssen definiert sein. Entspricht dem Check Geometrie Knoten.
- IMAGE
- Ein Abbild einer ganzen Komponente, eines Unterelements oder einer Region darin. Entspricht dem Check Abbild Knoten.
Die Klasse CheckData
und ihre Unterklassen
Die Klasse de.qfs.apps.qftest.shared.data.check.CheckData
und ihre
Unterklassen, alle aus demselben Package, komplettieren die Checker
-API.
Ein CheckData
Objekt kapselt die eigentlichen Daten für einen Check und
muss von der Methode Checker.getCheckData()
zurückgeliefert werden.
Hiermit werden die Daten zwischen QF-Test und dem SUT ausgetauscht. Für jeden
CheckDataType
gibt es eine zugehörige Unterklasse von
CheckData
. Sie müssen nur deren Konstruktoren kennen, also führen wir
auch nur diese auf:
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Darüber hinaus kann für ein ImageCheckData optional ein Algorithmus definiert werden.
|
|
||||||||
Die CheckerRegistry
Wenn Ihr Checker
implementiert und instanziiert ist, muss er bei der
CheckerRegistry
registriert werden. Die Klasse
de.qfs.apps.qftest.extensions.checks.CheckerRegistry
bietet hierzu
folgende Methoden:
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Beispiel für einen Checker
Das folgende Jython SUT-Skript zeigt, wie man alles zu einem eigenen
Checker zusammenfügt. Nehmen wir an, Sie haben eine Java-Swing-Anwendung und
möchten alle Labels in einem Panel gleichzeitig überprüfen. Dazu müssen Sie über
alle Komponenten im Panel und deren Kind-Komponenten iterieren, dabei die
Label-Komponenten identifizieren und eine Liste mit den enthaltenen Texten
generieren. In QF-Test Notation heißt das, Sie brauchen einen
CheckDataType.STRING_LIST
Checktyp und müssen die Daten in einem
StringItemsCheckData
Objekt zurückliefern:
from de.qfs.apps.qftest.extensions import ResolverRegistry from de.qfs.apps.qftest.extensions.checks import CheckerRegistry, \ Checker, DefaultCheckType, CheckDataType from de.qfs.apps.qftest.extensions.items import ItemRegistry from de.qfs.apps.qftest.shared.data.check import StringItemsCheckData from de.qfs.lib.util import Pair from java.lang import String import jarray componentClass = "javax.swing.JPanel" allLabelsCheckType = DefaultCheckType("AllLabels", CheckDataType.STRING_LIST, "Alle Labels im Panel") class AllLabelsChecker(Checker): def __init__(self): pass def getSupportedCheckTypes(self, com, item): return jarray.array([allLabelsCheckType], DefaultCheckType) def getCheckData(self, com, item, checkType): if allLabelsCheckType.getIdentifier() == checkType.getIdentifier(): labels = self._findLabels(com) labels = map(lambda l: l.getText(), labels) values = jarray.array(labels, String) return StringItemsCheckData(checkType.getIdentifier(), values) return None def getCheckDataAndItem(self, com, item, checkType): data = self.getCheckData(com, item, checkType) if data is None: return None return Pair(data, None) def _findLabels(self, com, labels=None): if labels is None: labels = [] if ResolverRegistry.instance().isInstance(com, "javax.swing.JLabel"): labels.append(com) for c in com.getComponents(): self._findLabels(c, labels) return labels def unregister(): try: CheckerRegistry.instance().unregisterChecker( componentClass, allLabelsChecker) except: pass def register(): unregister() global allLabelsChecker allLabelsChecker = AllLabelsChecker() CheckerRegistry.instance().registerChecker( componentClass, allLabelsChecker) register()
Nach Ausführung des Skripts findet man einen neuen Eintrag "Alle Labels im Panel" im
Checktyp Menü, sobald man im Checkaufnahmemodus mit der rechten Maustaste auf eine
JPanel
Komponente klickt (cf. Abschnitt 4.3). Wenn
Sie den allLabelsChecker
überall in Ihrer Clientanwendung verwenden
möchten, können Sie das obige SUT-Skript hinter den Warten auf Client
Knoten in der Vorbereitung stellen. Ansonsten können Sie den Checker auch nach
Bedarf registrieren und später in einem anderen SUT-Skript wieder
entfernen:
from de.qfs.apps.qftest.extensions.checks import CheckerRegistry global allLabelsChecker def unregister(): try: CheckerRegistry.instance().unregisterChecker( "javax.swing.JPanel", allLabelsChecker) except: pass unregister()