3.1+53.4
Implementierung eigener Unterelemente mit dem ItemResolver Interface

Wie in Abschnitt 5.9 beschrieben, ist QF-Test in der Lage, jenseits der Struktur von GUI Elementen mit Unterelementen zu arbeiten, die selbst keine GUI Elemente sind, z.B. die Zellen einer Tabelle, Knoten in einem Baum oder Zeichnungen auf einem Canvas. Solche Unterelemente werden mit Hilfe des ItemResolver Mechanismus implementiert, über den Sie auch Ihre eigenen spezifischen Unterelemente erstellen können.

Das ItemResolver Interface ist deutlich komplexer als die einfachen NameResolver2 oder FeatureResolver2 Interfaces aus dem vorhergehenden Abschnitt und es kann nicht ohne gute Programmierkenntnisse und Verständnis der zu Grunde liegenden Konzepte implementiert werden. Außerdem sind ItemResolver und die im nächsten Abschnitt beschriebenen Checker eng miteinander verknüpft und sollten gemeinsam implementiert werden, wenn Sie Ihre Unterelemente auch überprüfen können wollen.

Lassen Sie sich davon aber nicht abschrecken, denn andererseits ist dieser Mechanismus sehr mächtig und wenn Ihre ItemResolver erst einmal implementiert und registriert sind, integrieren sie sich so nahtlos in QF-Test, dass kein Unterschied zwischen standard und nicht-standard Unterelementen zu erkennen ist.

Sie finden im Verzeichnis qftest-8.0.1/Jython/Lib unter QF-Tests Wurzelverzeichnis einige Beispielimplementierungen, wie ktable.py oder gef.py. Beide Resolver sind zwar für SWT spezifische Tabellen, das Konzept allerdings ist bei allen Engines das gleiche.

ItemResolver Konzepte

Bevor Sie mit der Umsetzung eines ItemResolvers beginnen können, müssen Sie sich über die Art der Unterelemente klar werden, die Ihr GUI Element enthalten kann. Es könnte mehr als eine Art geben, so dass diese Entscheidung willkürlich ist. So haben wir z.B. Unterelemente für Swing, SWT und JavaFX Tabellen so implementiert, dass sowohl einzelne Zellen, als auch ganze Spalten als Unterelemente unterstützt werden. Letzteres ist sehr hilfreich, weil damit Checks für komplette Spalten möglich sind.

Als nächstes müssen Sie entscheiden, wie Sie die Unterelemente intern darstellen wollen. Sie können jede Art von Object verwenden, da QF-Test niemals auf Ihre interne Repräsentation zugreift, sondern diese lediglich zwischen den Methoden Ihres ItemResolvers hin und her reicht. Was am besten funktioniert, hängt von der API des GUI Elements ab. Wenn dieses bereits ein Konzept für Unterelemente bietet, ist es vermutlich am besten, diese Klassen zu verwenden.

Die wichtigste Entscheidung ist, wie Sie ein Unterelement gegenüber QF-Test repräsentieren. Wie in Abschnitt 5.9 beschrieben, kann der Anwender ein Unterelement über einen numerischen oder einen textuellen Index adressieren, wobei letzterer auch ein regulärer Ausdruck (vgl. Abschnitt 48.3) sein kann. Sie müssen also zwischen einem Unterelement und seinem Index (oder seinen Indizes) eine bidirektionale Abbildung ermöglichen, d.h. Sie müssen folgende Fragen beantworten können:

  • Was ist der numerische und was der textuelle Index für ein gegebenes Unterelement?
  • Welches Unterelement passt am besten zu einem gegebenen numerischen oder textuellen Index?

Die Kernpunkte bei der Vergabe von Namen für Unterelemente sind die gleichen wie bei normalen GUI Elementen. Arbeiten Sie hierzu bitte Abschnitt 5.4.2.1 und Abschnitt 5.4.2.2 gründlich durch bevor Sie fortfahren.

Ein einzelner numerischer oder textueller Index wird durch ein SubItemIndex Objekt repräsentiert. Das aktuelle Konzept unterstützt die Adressierung eines Unterelements mittels eines primären und eines sekundären Index. In der Zukunft hoffen wir, Indizes mit beliebiger Tiefe behandeln zu können, so dass ein Knoten in einem Baum statt über einen einzelnen Pfad auch über einen gemischten Index wie "tree@Root&1%Name:.*" angesprochen werden könnte. Daher ist der gesamte Index ein Array von SubItemIndex Objekten, auch wenn dessen Länge aktuell auf ein oder zwei Objekte beschränkt ist.

Die meisten Unterelemente haben eine Geometrie, also eine Position und eine Größe. Die Koordinaten eines Unterelements beziehen sich immer auf die obere linke Ecke des GUI Elements, zu dem sie gehören, unabhängig davon, ob dieses gescrollt wird. Dadurch hängen die Koordinaten nicht von der aktuellen Scrollposition ab. Für Unterelemente, für die Geometrie keinen Sinn macht oder nicht ermittelt werden kann, können Koordinaten ignoriert werden und die Methoden getItemLocation und getItemSize sollten einfach [0,0] zurückliefern.

Das ItemResolver Interface

Die Methoden des Interface de.qfs.apps.qftest.extensions.items.ItemResolver teilen sich in drei Kategorien auf: Erkennen eines Unterelements, die Beziehung zwischen einem Unterelement und seinem Index und dem Auslesen von Informationen aus bzw. Ausführen von Aktionen auf einem Unterelement.

 
 
Object getItem(Object element, int x, int y)
Liefert ein Unterelement für ein GUI Element an einer bestimmten Position. Für Unterelemente ohne Geometrie können die Koordinaten ignoriert und das Unterelement aufgrund von anderen Eigenschaften bestimmt werden, z.B. anhand der Selektion.
Parameter
elementDas GUI Element, dessen Unterelement bestimmt werden soll.
xDie X Koordinate relativ zum GUI Element.
yDie Y Koordinate relativ zum GUI Element.
Rückgabewert Ein beliebiges Objekt, welches das Unterelement repräsentiert, oder null falls es kein Unterelement an der angegebenen Stelle gibt.
 
int getItemCount(Object element, Object item)
Liefert die Anzahl der Unterelemente des GUI Elements auf der jeweils nächsten Ebene. Diese Methode wird aktuell zwar nicht verwendet, sollte aber wenn möglich korrekt implementiert werden. In der Zukunft könnte dies z.B. für einen 'Anzahl auslesen'-Knoten analog zu 'Index auslesen' oder 'Text auslesen' genutzt werden.
Parameter
elementDas GUI Element, für das die Zahl der Unterelemente ermittelt werden soll.
item Null um die Anzahl der Unterelemente auf oberster Ebene zu erhalten, ein Unterelement um die Anzahl von dessen Unterelementen der nächsten Ebene zu ermitteln.
Rückgabewert Die Anzahl von Unterelementen oder -1 falls es keine weitere Ebene gibt.
 
Object getItemForIndex(Object element, SubItemIndex[] idx)
Liefert ein Unterelement zu einem gegebenen Index.
4.0+ Am Ende dieser Prozedur ist ggf. ein Aufruf von setIndexesResolved notwendig, falls mehr als ein Index aufgelöst wurde.
Parameter
elementDas GUI Element, dessen Unterelement bestimmt werden soll.
idxDer Index oder die Indizes für das Unterelement.
RückgabewertDas Unterelement, das dem Index am besten entspricht.
Exceptions
IndexNotFoundException Falls kein Unterelement zum angegebenen Index passt. Verwenden Sie für diesen Fall den Konstruktor de.qfs.apps.qftest.shared.exceptions.IndexNotFoundException(SubItemIndex).
 
SubItemIndex[] getItemIndex(Object element, Object item, int type)
Liefert den/die SubItemIndex(es) für ein Unterelement eines GUI Elements.
Parameter
elementDas GUI Element, zu dem das Unterelement gehört.
itemDas Unterelement dessen Index ermittelt werden soll.
type Die Art des zu bestimmenden Index. Mögliche Werte sind INTELLIGENT, AS_STRING und AS_NUMBER, alle in der Klasse SubItemIndex definiert. Sofern nicht nur eine Art von Index unterstützt wird, sollte ein textueller Index für AS_STRING und ein numerischer Index für AS_NUMBER geliefert werden. Beim Typ INTELLIGENT können Sie frei entscheiden, welcher Art von Index das Unterelement am besten repräsentiert. Auch ein gemischter Index ist möglich, z.B. ein Spaltentitel plus numerischer Zeilenindex für eine Zelle in einer Tabelle.
Rückgabewert Ein Array von SutItemIndex Objekten. Aktuell sind nur Arrays mit einem oder zwei Elementen erlaubt.
 
int[] getItemLocation(Object element, Object item)
Liefert die Position eines Unterelements relativ zum GUI Element.
Parameter
elementDas GUI Element, zu dem das Unterelement gehört.
itemDas Unterelement dessen Position ermittelt werden soll.
Rückgabewert Die Position des Unterelements als int Array der Form [X,Y]. Die Position muss immer relativ zur oberen linken Ecke des GUI Elements sein, auch wenn diese Ecke aktuell nicht sichtbar ist, z.B. wenn das GUI Element gescrollt wird. Für Unterelemente ohne Geometrie liefern Sie einfach [0,0].
 
int[] getItemSize(Object element, Object item)
Liefert die Größe eines Unterelements relativ zum GUI Element.
Parameter
elementDas GUI Element, zu dem das Unterelement gehört.
itemDas Unterelement dessen Größe ermittelt werden soll.
Rückgabewert Die Größe des Unterelements als int Array der Form [Breite,Höhe]. Für Unterelemente ohne Geometrie liefern Sie einfach [0,0].
 
String getItemValue(Object element, Object item)
Liefert den Wert eines Unterelements für einen Text-Check.
Parameter
elementDas GUI Element, zu dem das Unterelement gehört.
itemDas Unterelement dessen Wert ermittelt werden soll.
Rückgabewert Ein String entsprechend dem Wert des Unterelements, sein Inhalt, Label, etc.
 
Boolean repositionMouseEvent(Object element, Object item, int[] pos)
Ändert die Koordinaten eines Mausevents auf ein Unterelement eines GUI Elements zu einer standard Position, üblicherweise die Mitte der Komponente, sofern die Koordinaten bei der Wiedergabe keine Rolle spielen. Diese Methode wird nur aufgerufen, wenn die Option Mausevents ohne Koordinaten aufnehmen wo möglich aktiviert ist. Ob es sicher ist, die Koordinaten zu überschreiben, kann von den ursprünglichen Koordinaten abhängen. So ändert QF-Test z.B. die Koordinaten für Events auf einen Baumknoten ab, sofern diese positiv sind und damit in den Knoten zeigen. Negative Koordinaten könnten einen Klick auf den Schalter zum Ein-/Ausklappen bedeuten und bleiben unverändert.
Parameter
elementDas GUI Element, zu dem das Unterelement gehört.
itemDas Ziel-Unterelement für den Event.
pos Die Koordinaten des Events als int Array der Form [X,Y]. Die Werte können direkt im Array manipuliert werden. Sie können entweder gezielt Koordinaten für einen bestimmten Punkt angeben oder [Integer.MAX_VALUE,Integer.MAX_VALUE] um die Koordinaten zu ignorieren, so dass der Event später auf die Mitte abgespielt wird.
Rückgabewert Boolean.TRUE falls die Position geändert wurde, Boolean.FALSE falls die Position nicht geändert wurde. Null um zu signalisieren, dass dieser Resolver das Element nicht behandelt.
Exceptions
BadItemException Falls das Element und der Item Typ nicht zusammen passen (sollte niemals passieren).
 
Boolean scrollItemVisible(Object element, Object item, int x, int y)
Scrollt ein GUI Element so, dass ein Unterelement sichtbar wird. Wenn möglich sollte das Unterelement komplett sichtbar gemacht werden. Falls es nicht in den sichtbaren Bereich passt, sollte zumindest die angegebene Position dargestellt werden. In den meisten Fällen können Sie einfach null zurückgeben und QF-Test das Scrollen selbst überlassen. Manchmal kann Scrollen allerdings nicht generisch implementiert werden, z.B. bei SWT Tabellen mit sichtbarem Header. Für GUI Elemente, die Scrollen gar nicht unterstützen, liefern Sie einfach Boolean.FALSE. Wird Boolean.TRUE geliefert, ruft QF-Test die Methode nach kurzer Zeit ein weiteres mal auf, da SWT nicht immer die gewünschte Position übernimmt. Im Idealfall sollte die Position dann stimmen und FALSE geliefert werden. Nach drei Versuchen mit TRUE gibt QF-Test aus und melden einen Fehler.
Parameter
elementDas GUI Element, zu dem das Unterelement gehört.
itemDas Unterelement, das sichtbar gemacht werden soll.
x Die X-Koordinate relativ zum Unterelement, die in jedem Fall sichtbar sein muss.
y Die Y-Koordinate relativ zum Unterelement, die in jedem Fall sichtbar sein muss.
Rückgabewert Boolean.TRUE falls die Scroll-Position des GUI Elements verändert wurde, Boolean.FALSE falls die Scroll-Position unverändert ist oder nicht gescrollt werden kann. Null um zu signalisieren, dass QF-Test das GUI Element selbst scrollen soll.
 
void setIndexesResolved(int num)
Informiert die Registry über die Anzahl der während der Indexbestimmung in getItemForIndex aufgelösten Indizes. Wird diese Methode am Ende von getItemForIndex nicht aufgerufen, geht die Registry von einem Index aus.
Parameter
num Die Anzahl der aufgelösten Indizes.
 
 

Die Klasse SubItemIndex

Wie im vorhergehenden Abschnitt erklärt, repräsentiert ein de.qfs.apps.qftest.shared.data.SubItemIndex einen (partiellen) Index für ein Unterelement eines GUI Elements. Diese Klasse definiert einige Konstanten mit folgender Bedeutung:

STRING
Dies ist ein textueller Index
NUMBER
Dies ist ein numerischer Index
REGEXP
Dies ist ein regulärer Ausdruck (vgl. Abschnitt 48.3), passend zu einem textuellen Index
INTELLIGENT
Ermittle den Typ von Index, der am besten zum Unterelement passt
AS_STRING
Ermittle einen textuellen Index
AS_NUMBER
Ermittle einen numerischen Index

Sie stellt außerdem folgende Methoden bereit:

 
 
SubItemIndex SubItemIndex(String index)
Erstellt einen neuen SubItemIndex vom Typ STRING.
Parameter
indexDer textuelle Index.
 
SubItemIndex SubItemIndex(int index)
Erstellt einen neuen SubItemIndex vom Typ NUMBER.
Parameter
indexDer numerische Index.
 
int asNumber()
Liefert den Index als Zahl.
RückgabewertDer numerische Index.
Exceptions
IndexFormatException Falls der Index nicht vom Typ NUMBER ist oder nicht als Integer geparst werden kann.
 
String getIndex()
Liefert den Index als String.
RückgabewertDer Index, konvertiert in einen String.
 
String getType()
Liefert den Typ des Index.
Rückgabewert Der Typ des Index, STRING, NUMBER oder REGEXP.
 
boolean matches(String name)
Prüft, ob der Index zum Namen eines Unterelements passt.
Parameter
nameDer zu prüfende Name.
Rückgabewert True falls der Index nicht numerisch ist und zum angegebenen Namen passt.
Exceptions
IndexFormatException Falls der Index einen ungültigen regulären Ausdruck enthält.
 
 

Die ItemRegistry

Wenn Ihr ItemResolver implementiert und instanziiert ist, muss er bei der ItemRegistry registriert werden. Die Klasse de.qfs.apps.qftest.extensions.items.ItemRegistry bietet hierzu folgende Methoden:

 
 
static ItemRegistry instance()
Es gibt immer nur ein einziges ItemRegistry Objekt und diese Methode ist der einzige Weg, Zugriff auf diese Singleton Instanz erlangen.
RückgabewertDie ItemRegistry Singleton Instanz.
 
void registerItemNameResolver2(Object element, ItemNameResolver2 resolver)
Registriert einen ItemNameResolver2 für ein spezifisches GUI Element. Der Resolver beeinträchtigt nicht die Garbage-Collection und wird automatisch entfernt, wenn die Komponente nicht mehr erreichbar ist.
Parameter
element Das GUI Element für das registriert wird.
resolver Der zu registrierende Resolver.
 
void registerItemNameResolver2(String clazz, ItemNameResolver2 resolver)
Registriert einen ItemNameResolver2 für eine spezifische Klasse von GUI Elementen.
Parameter
clazz Der Name der Klasse für die registriert wird.
resolver Der zu registrierende Resolver.
 
void registerItemResolver(Object element, ItemResolver resolver)
Registriert einen ItemResolver für ein spezifisches GUI Element. Der Resolver beeinträchtigt nicht die Garbage-Collection und wird automatisch entfernt, wenn die Komponente nicht mehr erreichbar ist.
Parameter
element Das GUI Element für das registriert wird.
resolver Der zu registrierende Resolver.
 
void registerItemResolver(String clazz, ItemResolver resolver)
Registriert einen ItemResolver für eine spezifische Klasse von GUI Elementen.
Parameter
clazz Der Name der Klasse für die registriert wird.
resolver Der zu registrierende Resolver.
 
void registerItemValueResolver2(Object element, ItemValueResolver2 resolver)
Registriert einen ItemValueResolver2 für ein spezifisches GUI Element. Der Resolver beeinträchtigt nicht die Garbage-Collection und wird automatisch entfernt, wenn die Komponente nicht mehr erreichbar ist.
Parameter
element Das GUI Element für das registriert wird.
resolver Der zu registrierende Resolver.
 
void registerItemValueResolver2(String clazz, ItemValueResolver2 resolver)
Registriert einen ItemValueResolver2 für eine spezifische Klasse von GUI Elementen.
Parameter
clazz Der Name der Klasse für die registriert wird.
resolver Der zu registrierende Resolver.
 
void unregisterItemNameResolver2(Object element, ItemNameResolver2 resolver)
Entfernt einen ItemNameResolver2 für ein spezifisches GUI Element.
Parameter
element Das GUI Element für das entfernt wird.
resolver Der zu entfernende Resolver.
 
void unregisterItemNameResolver2(String clazz, ItemNameResolver2 resolver)
Entfernt einen ItemNameResolver2 für eine spezifische Klasse von GUI Elementen.
Parameter
clazz Der Name der Klasse für die entfernt wird.
resolver Der zu entfernende Resolver.
 
void unregisterItemResolver(Object element, ItemResolver resolver)
Entfernt einen ItemResolver für ein spezifisches GUI Element.
Parameter
element Das GUI Element für das entfernt wird.
resolver Der zu entfernende Resolver.
 
void unregisterItemResolver(String clazz, ItemResolver resolver)
Entfernt einen ItemResolver für eine spezifische Klasse von GUI Elementen.
Parameter
clazz Der Name der Klasse für die entfernt wird.
resolver Der zu entfernende Resolver.
 
void unregisterItemValueResolver2(Object element, ItemValueResolver2 resolver)
Entfernt einen ItemValueResolver2 für ein spezifisches GUI Element.
Parameter
element Das GUI Element für das entfernt wird.
resolver Der zu entfernende Resolver.
 
void unregisterItemValueResolver2(String clazz, ItemValueResolver2 resolver)
Entfernt einen ItemValueResolver2 für eine spezifische Klasse von GUI Elementen.
Parameter
clazz Der Name der Klasse für die entfernt wird.
resolver Der zu entfernende Resolver.
 
 

Standard Repräsentation von Unterelementen

Für die Implementierung der Interfaces ItemNameResolver2, ItemValueResolver2 und Checker ist es wichtig, die Art der Objekte zu kennen, die ein Unterelement intern repräsentieren, denn dies sind die Objekte, die an die Methoden getItemName, getItemValue, getCheckData und getCheckDataAndItem übergeben werden.

JavaFX Die folgende Tabelle führt die komplexen Komponenten von JavaFX und die von QF-Test's standard ItemResolvern verwendete Repräsentation der jeweiligen Unterelemente auf.

GUI element classItem type
AccordionInteger Index
ChoiceBoxInteger Index
ComboBoxInteger Index
ListViewInteger Index
TabPaneInteger Index
TableViewint Array [column,row] wobei row < 0 eine ganze Spalte repräsentiert
TableHeaderRowInteger Spaltenindex
TextAreaInteger Zeile
TreeViewTreeItem Objekt
Tabelle 53.1:  Interne Repräsentation für Unterelement von JavaFX Komponenten

Swing Die folgende Tabelle führt die komplexen Komponenten von Swing und die von QF-Test's standard ItemResolvern verwendete Repräsentation der jeweiligen Unterelemente auf.

GUI Element KlasseArt des Unterelements
JComboBoxInteger Index
JListInteger Index
JTabbedPaneInteger Index
JTableint Array [column,row] wobei row < 0 eine ganze Spalte repräsentiert
JTableHeaderInteger Spaltenindex
JTextAreaInteger Zeile
JTreeTreePath Pfad
Tabelle 53.2:  Interne Repräsentation für Unterelement von Swing Komponenten

SWT Die folgende Tabelle führt die komplexen GUI Elemente von SWT und die von QF-Test's standard ItemResolvern verwendete Repräsentation der jeweiligen Unterelemente auf.

GUI Element KlasseArt des Unterelements
CComboInteger Index
ComboInteger Index
CTabFolderInteger Index
ListInteger Index
StyledTextInteger Zeile
TabFolderInteger Index
Tableint Array [Spalte,Zeile] oder nur Integer um eine Spalte zu repräsentieren
TextInteger Zeile
TreeObject Array [Integer Spalte,TreeItem Zeile] oder nur Integer um eine Spalte zu repräsentieren
Tabelle 53.3:  Interne Repräsentation für Unterelement von SWT GUI Elementen

Web Die folgende Tabelle führt die komplexen GUI Elemente für Web und die von QF-Test's standard ItemResolvern verwendete Repräsentation der jeweiligen Unterelemente auf.

GUI Element KlasseArt des Unterelements
SELECT KnotenOPTION Knoten
TEXTAREA KnotenInteger Zeile
Tabelle 53.4:  Interne Repräsentation für Unterelement von Web GUI Elementen