19. September 2019
Der Einfluss der Geometrie auf die Komponentenerkennung
... und wie wir sie beeinflussen können
Dieser Blogbeitrag befasst sich mit den Geometrieinformationen von Komponenten, warum QF-Test diese erfasst, welche Auswirkung diese haben und wie sie - auch bereits während der Aufnahme - geändert werden können.
Wenn Sie an weiterführenden Informationen zu diesem Thema interessiert sind, sollten Sie einen Blick in das Handbuch, Abschnitt "Wiedererkennung von Komponenten" werfen:
Dort wird der Algorithmus zur Komponentenerkennung im Detail erläutert.
Dazu eine kurze Zusammenfassung:
Basierend auf der den Informationen zu einer Komponente, die in der Testsuite gespeichert sind, bestimmt QF-Test die Übereinstimmungswahrscheinlichkeit (match probability) jeder sichtbaren Komponente im zu testendem Programm (SUT).
Die Komponente mit dem höchsten Wahrscheinlichkeitswert gewinnt diesen Vergleich, vorausgesetzt dieser Wert liegt über einem konfigurierbaren Mindestwert.
Ansonsten wird eine "ComponentNotFoundException" geworfen. Obwohl dieser Algorithmus im Laufe der Zeit feingeschliffen und poliert wurde, hat sich seit QF-Tests Ursprüngen nichts an dessen Grundkonzept geändert und die Standardeinstellungen haben sich im Großteil der Fälle bewährt.
Die Geometrie ist die am wenigsten relevante Information bei der Komponentenerkennung, kann aber entscheidend sein, falls die anderen Merkmale nicht ausreichen.
Wird eine Komponente überprüft, startet sie mit einem Wahrscheinlichkeitswert von 99,9%. Dann wird die Geometrie auf eine der folgenden drei Arten angewendet:
- Jeder der Werte - sofern festgelegt und ohne X und Y bei Fenstern - wird mit den tatsächlichen Werten verglichen und eine Abweichung reduziert die Wahrscheinlichkeit.
- Wichtig: Wenn die Daten zur Geometrie aus der Suite entfernt werden, wird die Geometrie daher bei allen Komponenten als gleich gewertet, mit perfekter Übereinstimmung.
- Wenn mindestens einer der Werte auf "-" gesetzt wird, wird die Übereinstimmung bei der Geometrie auf 0 reduziert, womit wird die Geometrie ebenfalls bei allen Komponenten gleich gewertet wird, allerdings mit Wahrscheinlichkeit 0, so dass andere Charakteristiken der Komponente übereinstimmen müssen.
Praktisches Beispiel:
Genug Theorie - Zeit für ein praktisches Beispiel, um das Vorgehen des Algorithmus zu verstehen:
Hinweis: Falls Ihre Lizenz nicht die Swing-Engine beinhaltet, wird das hier verwendete CarConfigurator-Demoprogramm nicht funktionieren. Um dieses Beispiel trotzdem selbst auszuprobieren, können Sie QF-Test mit dem Kommando "qftest -license=" im Demomodus starten.
- Öffnen Sie die Beispieltestsuite "carconfig_de.qft", zu finden unter Hilfe → Beispiel-Testsuites erkunden → Swing CarConfig Suite.
- Um das SUT zu starten expandieren Sie den Testfallsatz, wählen Sie den Bezug auf die Abhängigkeit "dependencies.SUT_Gestartet_Zurückgesetzt()") und drücken Sie [Eingabe] oder den "Wiedergabe starten"-Knopf in der Werkzeugleiste. Das QF-Test CarConfig Demo sollte nun starten und der Aufnahme-Knopf dabei aktiviert werden.
- Öffnen Sie eine neue, leere Testsuite und nehmen Sie drei Mausklicks auf die Anzeigefelder der Werte von "Preis Basismodell", "Preis Sonderausstattung" und "Rabatt" auf.
Die aufgenommenen Komponenten sollten etwa so aussehen wie auf folgendem Screenshot:
Wie nicht anders zu erwarten bei einem Demoprogramm erhalten wir ein Überangebot an Informationen zu den Komponenten - neben perfekt gesetztem Namen, Merkmal und weiteren Merkmalen auch aktuelle Informationen zu Struktur und Geometrie.
- Wählen Sie den Mausklick auf JCarConfigurator.DiscountValue aus, aktivieren Sie die Option "als "harten" Event wiedergeben" und führen Sie ihn mit [Enter] aus. Der Klick sollte genau treffen.
Lernen durch Spielen: Ändern der Komponenteninformationen
Um die Auswirkungen der Komponenteninformation auf die Wiedererkennung zu verstehen, führen wir jetzt einige Änderungen an diesen durch:
1. Kein Name
Entfernen Sie den Namen "DiscountValue" aus den Informationen der Komponente und spielen Sie den letzten Mausklick erneut ab.
→ Funktioniert. Aber wie gut? Um das zu sehen, öffnen Sie Bearbeiten → Optionen und wählen Sie Protokoll → Inhalt - dort setzen Sie die Option "Level für Meldungen im SUT" auf "Alle Meldungen". Vergessen Sie bitte nicht, dies am Ende wieder rückgängig zu machen, damit Ihre Protokolle nicht unnötig wachsen.
Jetzt erneut den Mausklick abspielen und das Protokoll über den Knopf oder [Strg+L] öffnen.
Expandieren Sie die Knoten via [Alt+Rechts] und wählen Sie "Looking for matching top-level components...". Diese Informationen sind nicht wirklich übersichtlich, sie sind primär für den QFS-Support im Fall von Schwierigkeiten bei der Komponentenwiedererkennung gedacht.
Scrollen Sie bis zum Ende; von dort aus wieder ein kleines Stück nach oben, bis Sie in etwa folgendes sehen:
.2 Calculating probability for
javax.swing.JTextField[DiscountValue,239,0,48x30,layout=javax.swing.plaf.basic.BasicTextUI$UpdateHandler,alignmentX=0.0,alignmentY=0.0,border=,flags=296,maximumSize=,minimumSize=,preferredSize=,caretColor=javax.swing.plaf.ColorUIResource[r=0,g=0,b=0],disabledTextColor=javax.swing.plaf.ColorUIResource[r=170,g=170,b=170],editable=true,margin=javax.swing.plaf.InsetsUIResource[top=1,left=2,bottom=2,right=2],selectedTextColor=javax.swing.plaf.ColorUIResource[r=0,g=0,b=0],selectionColor=javax.swing.plaf.ColorUIResource[r=172,g=210,b=248],columns=4,columnWidth=12,command=,horizontalAlignment=RIGHT]Geometry
probability: 99.9%
Structure match
Structure probability: 100%, penalty: 100%
Extra feature match
Extra feature probability: 100%, penalty: 100%
Feature match
Feature probability: 100%, penalty: 100%
Combined probability: 100%, acceptable
Final probability: 99%
Die Berechnung startet mit der "Geometry probability" als Ausgangswert, dann folgen - sofern vorhanden - Struktur, weitere Merkmale, Merkmal und Name wie im Handbuch beschrieben, wonach sich die "Final probability" ergibt.
2. Kein Merkmal
Entfernen Sie das Merkmal "Label: Rabatt" aus der Komponenteninformation. Spielen Sie wie zuvor den Mausklick ab und öffnen Sie wieder das Protokoll.
→ Funktioniert genauso gut wie vorher, nur der "Feature match" fehlt.
Geometry probability: 99.9%
Structure match
Structure probability: 100%, penalty: 100%
Extra feature match
Extra feature probability: 100%, penalty: 100%
Combined probability: 100%, acceptable
Final probability: 99%
3. Verändertes Merkmal
Machen wir das Experiment ein wenig interessanter: Machen Sie die letzte Änderung in der Testsuite rückgängig um das Merkmal wiederherzustellen, dann ändern Sie den Wert, z.B. indem Sie "blub" anhängen. Dann wieder Mausklick auswählen → [Enter] → [Strg+L]
→ Der Klick findet zwar immer noch sein Ziel, aber das Log zeigt ein "Feature mismatch" an; die "Combined probability" ist nur knapp über dem üblichen Minimum von 50%.
Geometry probability: 99.9%
Structure match
Structure probability: 100%, penalty: 100%
Extra feature match
Extra feature probability: 100%, penalty: 100%
Feature mismatch
Feature probability: 100%, penalty: 55%
Combined probability: 55%, acceptable
Final probability: 54.4%
4. Verändertes Merkmal, weiteres Merkmal und veränderte Struktur
Versuchen wir, die Wiedererkennung endgültig zu zerstören: Ändern Sie "Insgesamt" (bei Struktur) auf 5, als ob ein vorher existierendes Textfeld in der Zwischenzeit entfernt wurde und hängen Sie ein weiteres "blub" an den Wert von qfs:label" (Weitere Merkmale). Letztendlich sollte die verstümmelte Komponente so aussehen:
→ Erstaunlicherweise trifft der Mausklick immernoch das richtige Textfeld und die "Combined probability" bleibt konsistent, obwohl das Protokoll drei "Warnungen beim Auflösen der Komponente" anzeigt.
Geometry probability: 99.9%
Structure mismatch index: 2, count: 4
Structure probability: 99.9%, penalty: 60%
Extra feature mismatch
Extra feature probability: 99.9%, penalty: 55%
Feature mismatch
Feature probability: 99.9%, penalty: 55%
Combined probability: 54.9%, acceptable
Final probability: 54.4%
Anders als von vielen Schülern vermutet, hilft uns hier die Geometrie im echten Leben und erlaubt uns, den Test erfolgreich auszuführen. Jetzt könnten wir die aktuellen, korrekten Informationen erhalten und unsere Komponente reparieren, indem wir die Komponente auswählen und per Rechtsklick → "Komponente(n) aktualisieren".
Probieren Sie dies ruhig aus, aber machen Sie das anschließend wieder rückgängig, um den gestörten Status wie im Bildschirmabbild wieder herzustellen.
5. Leere Geometrie - match everything
Nun wird es Zeit, endlich die Geometrie zu manipulieren. Da unsere Geometrie sowieso perfekt übereinstimmt, sollte das Entfernen von X, Y, Breite und Höhe ja nichts ändern (vgl. Zusammenfassung am Anfang).
→ Ups! Jetzt geht der Klick auf den Wert von "Preis Basismodell". Im Protokoll erkennen wir, dass alle vier Textfelder nun die gleiche "Final probability" von 54.4% besitzen. Logisch, alle starten ja mit dem selben Wert für die Übereinstimmung mit der Geometrie und haben die selbe Anzahl an Fehlern beim Auflösen der Komponente. Mangels besserer Optionen wählt QF-Test einfach die erste mögliche Komponente aus.
Wir sehen, dass das Entfernen von Geometriewerten riskant ist, da es leicht zu falsch positiven Treffern führt. Trotzdem kann die Möglichkeit, Geometrieattribute leer zu lassen, nützlich sein. Beispielsweise können Sie die einzelnen Textfelder auch ausschließlich über die Geometrie ansteuern, indem Sie den Y-Wert jeweils auf 30, 60, 90 oder 120 setzen.
6. Geometrie "-" - match nothing
Falsch positive Ergebnisse zu analysieren, kann schwierig sein, also möchten wir vielleicht die Erkennung auf Basis der Geometrie ganz vermeiden. Setzen Sie dazu eins oder alle Geometrieattribute auf "-".
→ OK, jetzt haben wir endlich genug kaputt gemacht, um eine ComponentNotFoundException zu erhalten. Das Protokoll zeigt wie erwartet, dass nun alle Kandidaten ein Wahrscheinlichkeit von Null besitzen.
7. Wiederaufbau - Struktur
Machen wir uns an den Wiederaufbau und reparieren die Struktur, indem wir "Insgesamt" wieder auf 4 setzen.
→ Das reicht nicht.
Das Protokoll zeigt an, dass der Match bei der Struktur uns zwar auf ein akzeptables Zwischenergebnis von 70% gebracht hat, dieses aber durch "Feature" und "Extra Feature mismatch" auf inakzeptable 38,5% reduziert wird.
8. Wiederaufbau - Merkmal
Machen Sie die letzte Änderung rückgängig um die Struktur wieder zu zerstören. Stellen Sie stattdessen das Merkmal wieder her, indem Sie es zurück auf "Label: Rabatt" setzen.
→ Der Klick funktioniert und trifft wieder.
Im Protokoll erkennen wir, dass (ohne Berücksichtigung des Namens) das Merkmal die höchste Relevanz hat und ausreicht, um die Wahrscheinlichkeit über das Minimum zu heben - trotz anderer "mismatches".
9. Zeit zum Ausprobieren
Probieren Sie ruhig noch andere Kombinationen an korrekten und verfälschten oder leeren Komponenteninformationen aus. So können Sie beispielsweise erkennen, dass die korrekten Angaben bei der Struktur alleine ausreichen, solange nicht ein anderer "mismatch" dagegen spricht.
Generische Komponenten
Hinweis: Die Lektüre des Kapitels "Wie erreicht man eine robuste Komponentenerkennung?" ist generell zu empfehlen. Im Abschnitt "Nicht notwendige Aufzeichnungen ersparen oder generische Komponenten einsetzen" () wird das Konzept der generischen Komponenten erklärt, das wir im Folgenden benötigen.
Nutzen wir nun das erlangte Wissen und erstellen eine generische Komponente, welche nur auf dem "Weiteren Merkmal" qfs:label basiert:
- Wählen Sie die Komponente TextField JCarConfigurator.DiscountValue aus und kopieren Sie diese eine Ebene höher in das Window JCarConfigurator. Wählen Sie "QF-Test IDs eindeutig machen" um ID-Konflikte zu vermeiden und ändern Sie dann die QF-Test ID des kopierten Knotens in "generischesTextField".
- Setzen Sie die Geometrie auf "-", entfernen Sie das Merkmal, die Struktur und alle weiteren Merkmale bis auf "qfs:label" und setzen Sie den Wert des letzteren auf $(label). Das Resultat sollte so aussehen:
- Um diese Komponente zu verwenden, erstellen Sie einen Testschritt im Extrasequenzenbereich mit einer Variable namens "label" und mit Wert "Rabatt". Kopieren Sie den Mausklick auf JCarConfigurator.DiscountValue in den Testschritt und ändern Sie die QF-Test ID der Komponente auf "generischesTextField". Das Ausführen des Mausklicks sollte direkt funktionieren.
- Warum und wo ist das nützlich? Fügen Sie einen Datentreiber mitsamt Datentabelle in den Testschritt ein. Erstellen Sie eine Spalte "label" innerhalb der Datentabelle und vier Reihen mit den Werten "Preis Basismodell", "Preis Sonderausstattung", "Rabatt" und "Endpreis" (wie hier zu sehen), starten Sie dann den gesamten Testschritt.
Zusammenfassung
- Geometriewerte in aufgenommenen Komponenten sind nützlich in Kombination mit anderen Merkmalen. Wenn Sie eher mit aufgenommenen Komponenten arbeiten, ändern Sie nichts. Falls die meisten aufgenommenen Komponenten keine verlässlichen Namen oder andere Merkmale besitzen, sollten Sie für eine festgelegte Fenstergröße beim Start des SUT sorgen, um die Ergebnisse konsistent zu halten.
- Alle Geometriewerte zu entfernen ist normalerweise eine schlechte Idee, die zu falschen Positiven führt, wenn sich Merkmale ändern. Um die Geometrie nicht in die Berechnung einfließen zu lassen, setzen Sie stattdessen alle Werte auf "-".
- Generische Komponenten sollten immer den Wert "-" bei Ihrer Geometrie nutzen, außer sie basieren ausdrücklich auf Koordinaten.
Falls Sie nun das Gefühl haben, dass Ihre Tests ohne Bezug auf Geometrie besser laufen, werden Sie sich vermutlich fragen, wie Sie effizient alle Werte auf "-" ändern können. Deshalb beende ich diesen Artikel mit einer Anleitung, mit deren Hilfe Sie dies bewerkstelligen können und zeige Ihnen ein bis jetzt nicht dokumentiertes Hilfsmittel, welches sicherstellt, dass neue Komponenten direkt so aufgenommen werden.
Ändern aller aufgenommener Geometriewerte
Hinweis: Bevor Sie irgendeine Art von Massenänderungen vornehmen empfiehlt es sich, die Testsuiten vorher zu sichern und ein Backup anzulegen, idealerweise mit einem System zur Versionskontrolle wie git!
Das ändern aller Werte in einer einzelnen Testsuite ist sehr einfach: Wählen Sie den "Fenster und Komponenten"-Knoten, drücken Sie [Strg+H] um den "Ersetzen"-Dialog zu öffnen. Nutzen Sie den fortgeschrittenen Dialog, indem Sie auf ">>" drücken, falls nötig, und setzen Sie die Werte wie unten zu sehen:
Stellen Sie sicher, dass sowohl "Reguläre Ausdrücke verwenden" (damit der reguläre Ausdruck ".*" funktioniert), als auch "Gesamtes Attribut vergleichen" ausgewählt ist. Klicken Sie dann auf "Alle ersetzen". Der Ästhetik halber können Sie dieses Vorgehen noch für X, Y, und Breite wiederholen - das ist allerdings unnötig. Ein Wert auf "-" reicht aus um den gewünschten Effekt zu erzielen.
Sie können dasselbe für alle Testsuiten in einem Projekt durchführen, indem Sie den "Ersetzen"-Dialog aus der Projektansicht heraus öffnen und die Reichweite der Ersetzen-Operation auf "Alle Testsuiten des Projektes" setzen. Es gibt zwar das kleine Problem, dass die Attribute X und Y auch bei Mauseventknoten vorkommen, aber bei diesen ist "-" kein zulässiger Wert, so dass daraus kein Schaden enstehen sollte.
Für den Fall, dass Sie die Geometrie nicht komplett ausschalten wollen, sondern nur teilweise basierend auf einer Bedingung (z.B. für Komponenten der Klasse TextField), lesen Sie bitte:
https://www.qftest.com/qf-test-handbuch/lc/manual-de-user_gui.html#usec_tunesearch
Hier wird beschrieben, wie die Suche mit dem Setzen von Marken kombiniert und wie die Ersetzen-Option auf diese Marken eingeschränkt werden kann.
Ändern, wie Geometrie aufgenommen wird
Zuletzt der versprochene Resolver, der die Geometrieeinstellungen bei der Aufnahme ändern kann. Er basiert auf einer internen API, die manchmal von unserem Support verwendet wird um die teilweise sehr speziellen Anforderungen unserer Kunden zu erfüllen. Für den allgemeinen Gebrauch ist diese API zu komplex, in diesem Fall ist sie allerdings sehr hilfreich.
Um diese Funktion zu aktivieren, fügen Sie folgenden "Groovy SUT Skript"-Knoten nach dem "Warten auf Client" Knoten in der Vorbereitung ein:
def getElementInfo(com, info) {
info.setX(Integer.MIN_VALUE)
info.setY(Integer.MIN_VALUE)
info.setWidth(Integer.MIN_VALUE)
info.setHeight(Integer.MIN_VALUE)
return
info
}
resolvers.addResolver(
"drop geometry"
,
this
.&getElementInfo)
Um die Auswirkungen des Resolvers auf Komponenten einer oder mehrerer Klassen zu beschränken, muss die addResolver-Methode entsprechend angepasst werden, beispielsweise so:
resolvers.addResolver("drop geometry", this.&getElementInfo, "TextField", "MenuItem")
Sie können auch innerhalb der Resolvermethode auf info.getClazz(), info.getName() oder info.getFeature() prüfen. Wie vorhin erwähnt, wenden Sie sich bei sehr speziellen Anforderungen bitte an unseren Support.