ExtJS-GUI mit Selenium testen
Besonderheiten Tipps und Kniffe für Seleniumtests von ExtJS-GUIs
In diesem Beitrag geht es um das Testen von ExtJS-basierten Web-GUIs mithilfe von Selenium. Was Selenium ist und wie es verwendet wird wurde bereits im Artikel PHPUnit und Selenium umrissen.
Problemstellung
Selenium ist ein wundervolles Werkzeug zum testen von Webanwendungen. Der große Vorteil besteht vor allem auch im Verhältnismäßig einfachen Aufzeichnen der Testcases im Browser. Naheliegenderweise möchte man das auch mit ExtJS besierten GUIs machen. Aber nach den ersten Gehversuchen tauchen unerwartete Probleme auf.
ExtJS – dynamisch erzeugte IDs
Die DOM-Elemente werden von ExtJS dynamisch erzeugt. Das bedeutet es gibt keine festen IDs oder dergleichen an die man sich mit Selenium hängen könnte. Wenn man jetzt einen Test aufzeichnet, der ein einfaches Formular ausfüllt und abschickt, lässt dieser sich nicht mehr automatisch abspielen. Der Grund:
Selenium IDE zeichnet soweit wie möglich Element-IDs auf. Diese werden von ExtJS dynamisch erzeugt und sind nur für die Laufzeit gültig. Das bedeutet, nach dem nächsten Reload oder bei erneutem Aufrufen der Webseite ändern sich die IDs und der Test schlägt fehl.
ExtJS – dynamisch erzeugte GUI-Elemente
Das ExtJS-Framework bietet eine ganze Reihe sehr interessanter GUI-Elemente. Das geht vom einfachen Schieberegler (Slider) bis zum komplexen TreeGridView (Tabelle mit aufklappbaren Baumstrukturen in den Zeilen). Selbstverständlich werden auch hier überall dynamische IDs verwendet, so dass ein automaschies aufzeichen von Tests mit der Selenium-IDE nicht so einfach möglich ist.
Neu ist aber für viele Webdesigner und Tester, dass ExtJS auch „normale“ HTML-Elemente teilweise durch eigene Lösungen ersetzt. So werden zum Beispiel Dropdown-Menus nicht mit
<select name="Fahrzeug">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
Sondern ExtJS erzeugt etwas wie:
<div class="x-form-field-wrap x-form-field-trigger-wrap" id="ext-gen1152" style="width: 381px; ">
<input type="hidden" name="fahrzeug" id="ext-gen1154" value="16144">
<input type="text" size="24" autocomplete="off" id="ext-comp-1348" class="x-form-text x-form-field x-trigger-noedit" readonly="" style="width: 351px; ">
<img src="image/gif" alt="" class="x-form-trigger x-form-arrow-trigger" id="ext-gen1153">
</div>
Es fällt auf, dass die Auswahlen für die Select-Box im Beispiel von ExtJS im Code noch gar nicht sichtbar sind. Dementsprechend gestaltet es sich nicht gerade einfach dort mit der Selenium-IDE erfolgreich Testcases aufzuzeichen.
Lösungsansätze
Obwohl es auf den ersten Blick sehr schwierig erscheint ExtJS basierte Oberflächen mit Selenium zu testen ist es nicht unmöglich. Auch wenn ExtJS zwar viele Sachen anders und Dynamisch löst, lässt sich doch hier und da ein gewisses Muster erkennen. Man kann sich also Schnipsel (sog. Snippets) erarbeiten die jeweils nur leicht modifiziert werden müssen. Hat man sich davon einmal einen gewissen Grundstock aufgebaut stellt sich schnell ein „gefühlter Break-Even“ ein. Da ExtJS viele Dinge via Ajax-Request läd ist es ausserdem sinnvoll auf jedes Element zu warten, das man benutzen möchte. Dazu gibt es den Befehl „waitForElementPresent“. Eine Übersicht der Selenese Befehle gibt es im Netz unter http://release.seleniumhq.org/selenium-core/1.0.1/reference.html.
Anpassbare Codeschnipsel (Snippets)
Im folgenden werden ein paar beispielhafte Snipptes aufgeführt zum testen bestimmter Elemente in ExtJS.
Buttons
Buttons bekommen keinen speziellen Namen oder eine eindeutige ID. Allerdings haben Sie meistens eine eindeutige Beschriftung. Dafür gibt es eine Selenese Mthode. Hat der Button die Beschriftung „abbrechen“ so erreicht man ihn mit Selenium per:
waitForElementPresent | //button[contains(text(),’abbrechen‘)] |
click | //button[contains(text(),’Login‘)] |
Menu aus Accordion-Layout und Treepanel
Ein Accordion-Layout eignet sich gut um Menus zu realisieren. In jedes „Fach“ kann ein Treepanel eingebunden werden, so dass mit wenig Platz viel Menu untergebracht werden kann. Aber wie kann man mit Selenium darin Navigieren ? Der Trick besteht hier darin, jedem „Fach“ eine ID (ExtJS-ID) zuzuweisen. Diese kann im Browser verwendet werden. Der Rest passiert dann von dort aus via XPath.
waitForElementPresent | //div[@id=’cbo_259244′]/div[@class=“x-panel-bwrap“]/div/ul/div/li[1]/div/img[1] |
click | //div[@id=’cbo_259244′]/div[@class=“x-panel-bwrap“]/div/ul/div/li[1]/div/img[1] |
Das innenliegende Treepanel bekommt ebenfalls für jeden „Node“ eine ID zugewiesen. Das „aufklappen“ das Baumes funktioniert dann sogar wenn ein anderes Fach im Accordion momentan geöffnet ist.
Select-Box
Angenommen es soll in einem Formular mit einer Select-Box für ein Zelt die Anzahl der Schlafplätze ausgewählt werden. Der Weg ist hier: lokalisieren der Elemente mit Name oder Wert, lokalisieren der Kindelemente per XPath. Jedes Element erst Nutzen wenn es vollständig geladen ist.
waitForElementPresent | //input[@name=’schlafplaetze‘]/following-sibling::img |
click | //input[@name=’schlafplaetze‘]/following-sibling::img |
waitForElementPresent | //div[text()=’4′ and not(ancestor::*[contains(@style,’display: none‘) or contains(@style, ‚visibility: hidden‘) or contains(@class,’x-hide-display‘)])] |
click | //div[text()=’4′ and not(ancestor::*[contains(@style,’display: none‘) or contains(@style, ‚visibility: hidden‘) or contains(@class,’x-hide-display‘)])] |