'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 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.
|
| | CheckData getCheckData(Object element, Object item, CheckType type) | |
Parameter | element |
Das GUI Element, für das die Checkdaten ermittelt werden sollen.
| item |
Ein optionales Unterelement, das geprüft werden soll. Sein Typ hängt von der Art
des GUI Elements und der dafür registrierten ItemResolver ab, wie in
Abschnitt 53.4.5 beschrieben.
| type |
Die Art des auszuführenden Checks.
| Rückgabewert |
Die Checkdaten für den aktuellen Zustand des GUI Elements selbst, falls
item null ist, oder des Unterelements. Der gewünschte Checktyp wird
über den Parameter type definiert, der normalerweise einer der vorher
von getSupportedCheckTypes zurückgelieferten Checktypen sein sollte.
Falls Sie den gewünschten Check für das angegebene Ziel nicht ausführen können,
liefern Sie null.
| | Pair getCheckDataAndItem(Object element, Object item, CheckType type) | |
Parameter | element |
Das GUI Element, für das die Checkdaten ermittelt werden sollen.
| item |
Ein optionales Unterelement, das geprüft werden soll. Sein Typ hängt von der Art
des GUI Elements und der dafür registrierten ItemResolver ab, wie in
Abschnitt 53.4.5 beschrieben.
| type |
Die Art des auszuführenden Checks.
| Rückgabewert |
Ein Pair bestehend aus den Checkdaten für das GUI Element oder
Unterelement sowie das Unterelement, auf das sich der Check bezieht. Letzteres kann
null sein.
| | CheckType[] getSupportedCheckTypes(Object element, Object item) | |
Parameter | element |
Das GUI Element, für das die möglichen Checks ermittelt werden.
| item |
Ein optionales Unterelement, das geprüft werden soll. Sein Typ hängt von der Art
des GUI Elements und der dafür registrierten ItemResolver ab, wie in
Abschnitt 53.4.5 beschrieben.
| Rückgabewert |
Ein Array mit den CheckType Objekten, die Ihr Checker
unterstützt. Das erste Element ist der Standard für die Check-Aufnahme mit einem
Linksklick. Falls item null ist, liefern Sie nur die Checks zurück,
die sich auf das GUI Element als ganzes beziehen. Ansonsten ist es am besten, alle
verfügbaren Checks mit oder ohne Unterelement zu liefern, da der Anwender zwar
vielleicht auf ein Unterelement geklickt hat, aber trotzdem einen Check für das
ganze GUI Element aufnehmen möchte.
| |
|
|
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:
|
| | Pair Pair(Object first, Object second) | |
Parameter | first | Das erste Objekt. Kann null sein. | second | Das zweite Objekt Kann null sein. | |
| Object getFirst() | |
Rückgabewert | Das erste Objekt. | | Object getSecond() | |
Rückgabewert | Das zweite Objekt. | |
|
|
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:
|
| | DefaultCheckType(String identifier, CheckDataType dataType, String description) | |
Parameter | identifier |
Der Bezeichner des Checks. Die Standardchecks von QF-Test benutzen hierfür
ausschließlich Kleinbuchstaben. Um Konflikte zu vermeiden, beginnen Sie Ihre
Bezeichner einfach groß.
| dataType |
Der CheckDataType für Ihre Checkdaten.
| description |
Die Beschreibung des Checks für das Checkmenü.
| |
|
|
|
Der Vollständigkeit halber führen wir auch die Methoden des CheckType
Interfaces auf:
|
| | CheckDataType getDataType() | |
Rückgabewert |
Der Datentyp des Checks.
| | String getDescription() | |
Rückgabewert |
Die Beschreibung für den Checktyp.
| | String getIdentifier() | |
Rückgabewert |
Der Bezeichner des Checktyps.
| |
|
|
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 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:
|
| | BooleanCheckData BooleanCheckData(String identifier, boolean value) | |
Parameter | identifier |
Der Bezeichner des Checktyps. Sollte normalerweise dem Bezeichner des
type Arguments entsprechen, das an Checker.getCheckData
übergeben wurde.
| value |
Der Wert des Checks, ein Boolean Zustand.
| |
| GeometryCheckData GeometryCheckData(String identifier, int x, int y, int width, int height) | |
Parameter | identifier |
Der Bezeichner des Checktyps. Sollte normalerweise dem Bezeichner des
type Arguments entsprechen, das an Checker.getCheckData
übergeben wurde.
| x |
Die X-Koordinate für den Check.
| y |
Die Y-Koordinate für den Check.
| width |
Die Breite für den Check.
| height |
Die Höhe für den Check.
| |
| ImageCheckData ImageCheckData(String identifier, ImageRep image, int xOffset, int yOffset, int subX, int subY, int subWidth, int subHeight) | |
Parameter | identifier |
Der Bezeichner des Checktyps. Sollte normalerweise dem Bezeichner des
type Arguments entsprechen, das an Checker.getCheckData
übergeben wurde.
| image |
Das Abbild für den Check. Näheres hierzu unter Abschnitt 53.10.
| xOffset |
Ein optionaler X-Offset.
| yOffset |
Ein optionaler Y-Offset.
| subX |
Die X-Koordinate einer optionalen Check-Region.
| subY |
Die Y-Koordinate einer optionalen Check-Region.
| subWidth |
Die Breite einer optionalen Check-Region.
| subHeight |
Die Höhe einer optionalen Check-Region.
| |
| SelectableItemsCheckData SelectableItemsCheckData(String identifier, Object[][] values) | |
Parameter | identifier |
Der Bezeichner des Checktyps. Sollte normalerweise dem Bezeichner des
type Arguments entsprechen, das an Checker.getCheckData
übergeben wurde.
| values |
Der Wert des Checks, ein Array von Arrays mit einem String und einem Boolean für
"regexp" und einem Boolean für "selected".
| |
| StringCheckData StringCheckData(String identifier, String value) | |
Parameter | identifier |
Der Bezeichner des Checktyps. Sollte normalerweise dem Bezeichner des
type Arguments entsprechen, das an Checker.getCheckData
übergeben wurde.
| value |
Der Wert des Checks, ein String.
| |
| StringItemsCheckData StringItemsCheckData(String identifier, String[] values) | |
Parameter | identifier |
Der Bezeichner des Checktyps. Sollte normalerweise dem Bezeichner des
type Arguments entsprechen, das an Checker.getCheckData
übergeben wurde.
| values |
Der Wert des Checks, ein Array von Strings.
| |
|
|
|
Darüber hinaus kann für ein ImageCheckData optional ein Algorithmus definiert werden.
|
| | void setAlgorithm(String algorithm) | |
|
|
|
|
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:
|
| | static CheckerRegistry instance() | |
Rückgabewert | Die CheckerRegistry Singleton Instanz. | | void registerChecker(Object element, Checker checker) | |
Parameter | element |
Das GUI Element für das registriert wird.
| checker |
Der zu registrierende Checker.
| |
| void registerChecker(String clazz, Checker checker) | |
Parameter | clazz |
Der Name der Klasse für die registriert wird.
| checker |
Der zu registrierende Checker.
| |
| void unregisterChecker(Object element, Checker checker) | |
Parameter | element |
Das GUI Element für das entfernt wird.
| checker |
Der zu entfernende Checker.
| |
| void unregisterChecker(String clazz, Checker checker) | |
Parameter | clazz |
Der Name der Klasse für die entfernt wird.
| checker |
Der zu entfernende 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() |
|
| | Beispiel 53.33: Alle Labels in einem Panel checken | |
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() |
|
| | Beispiel 53.34: Den Label Checker entfernen | |