Mittels Unit- und Integration-Tests kann die Qualität einer Grails Web-Applikation zum Großteil sichergestellt werden.
Um eine Funktionalität aus Nutzersicht zu überprüfen ist eine andere Herangehensweise erforderlich: Man startet die Applikation und überprüft per Browser ob die verschiedenen Anwendungsfälle wie gewünscht durchgeführt werden können.
Um langfristig zu gewährleisten, dass die Funktionalität erhalten bleibt, müsste man die Schritte nach jeder weiteren Code-Änderung wiederholen.
Dies ist umständlich bis unmöglich und kann in Grails mittels Geb automatisiert werden.
Anwendungsfälle von funktionalen Tests
Es gibt unendlich viele Möglichkeiten durch eine Web-Applikation zu navigieren. Dementsprechend sinnlos ist der Versuch “möglichst Alles” zu testen.
Folgende Szenarien sind aus meiner Sicht für “functional testing” interessant:
Sicherstellung von Kern-Funktionalität
Die Durchführbarkeit, zumindest jener Anwendungsfälle ohne welche die Web-Applikation nutzlos werden würde, kann gut über funktionale Tests sichergestellt werden.
Planung von Usability-Tests
Bei der Planung von Tests der Benutzerfreundlichkeit kann man den idealen Ablauf zur Lösung der Aufgabe definieren und damit sicherstellen, dass zumindest dieser Weg zum Zeitpunkt des Tests mit dem Teilnehmer funktionieren wird.
Regressionsvermeidung
Regressionen – für mich einer der Hauptgründe überhaupt Tests zu schreiben.
Für jeden gefundenen Programmfehler wird ein Test geschrieben, welcher diesen Fehler reproduziert. Dann implementiert man die Lösung so, dass der Test erfolgreich verläuft – natürlich ohne dabei den Test zu ändern.
Somit wird eine Applikation langfristig immer “besser”, da sich ein bereits gefundener Bug unmöglich erneut in das Programm einschleichen kann.
Starthilfe zur Geb-Entwicklung
Wie der Titel der offiziellen Dokumentation, “The Book of Geb”, vermuten lässt, ist Geb ist umfangreich dokumentiert.
Zumindest das Einführungskapitel solltest du an dieser Stelle gelesen haben.
Eine Sammlung weiterer wichtiger Ressourcen ist am Ende dieses Artikels zusammen gefasst.
Im Folgenden möchte ich auf einige Hürden und Tücken hinweisen um dir einen einfachen Einstieg in Geb zu ermöglichen.
Geb-Hürde 1: Abhängigkeitskonflikt verhindert sämtliche Grails Kommandos
Error executing script Clean: loader constraint violation: when resolving overridden method "org.apache.xerces.jaxp.SAXParserImpl.getParser()Lorg/xml/sax/Parser;" the class loader (instance of org/codehaus/groovy/grails/cli/support/GrailsRootLoader) of the current class, org/apache/xerces/jaxp/SAXParserImpl, and its superclass loader (instance of), have different Class objects for the type org/xml/sax/Parser used in the signature
Umgang mit der Hürde
Diesen Fehler konnte ich durch Ausschluss der Abhängigkeit zu xercesImpl
bei selenium-htmlunit-driver
beheben:
Das Problem ist im Grail – user Forum erläutert.
Geb-Hürde 2: Abstraktionsebenen erschweren den Einstieg
Beim Einsatz von Geb in Grails müssen viele Ebenen zusammen arbeiten:
- Grails Geb Plugin
- Eigentliches Geb
- Grails Testing Framework / Spock
- Selenium WebDriver
- Browser (HtmlUnit, Firefox, Chromium, IE)
Schwierigkeiten sind vor allem beim Grails Plugin (aufgrund der mangelhaften Dokumentation) und den Browsern oder “Browser-Drivers” wahrscheinlich.
Umgang mit der Hürde
Um das Grails Plugin möglichst schnell verstehen zu lernen ist Luke Daley’s Beispielprojekt auf GitHub hilfreich.
Übernimm am besten die Beispieldateien in dein Projekt und passe sie dann entsprechend an.
Geb-Hürde 2: Unzureichende Spezifizierung erschwert die Identifikation
Um innerhalb eines Tests überprüfen zu können, auf welcher Seite sich Geb gerade befindet, benötigt es gewisse Anhaltspunkte. Die Summe der Anhaltspunkte muss die jeweilige Seiten korrekt und vor allem eindeutig identifizieren.
Im oben genannten Beispielprojekt kannst du anhand der static at
-Deklarationen erkennen, dass die einzelnen Pages hauptsächlich über ihren Titel identifiziert werden.
Oft reicht dies aus.
Spätestens bei einer mehrsprachigen Applikation schlägt es in dieser Form allerdings fehl.
Das entsprechende Stacktrace ist wenig hilfreich. Unter anderem fehlt ein Hinweis auf welcher Seite man sich denn – anstelle der erwarteten – befindet.
Condition not satisfied: at LoginPage | false
junit.framework.AssertionFailedError: Condition not satisfied:
at LoginPage | false
Versäumst du die eindeutige Definition, öffnet das also viele Fehlerquellen.
Umgang mit der Hürde
Es ist also sinnvoll, eine Seite über zusätzliche Elemente zu identifizieren.
Geb-Hürde 3: jQuery 1.6 wird in HtmlUnit fehlerhaft ausgeführt
Bei funktionialen Tests wird ein am System verfügbarer Browser gesteuert.
Man sieht also beispielsweise das Firefox-Fenster in welchem die definierten Anweisungen durchlaufen werden.
Andererseits fungiert auch HtmlUnit als Browser, bietet allerdings die Möglichkeit die Äblaufe im Hintergrund vorzunehmen.
Aufgrunddessen ist HtmlUnit für Build-Automation die erste Wahl.
Verwendet die zu testende Applikation jQuery, kommt es bei den aktuellen Versionen (1.6.x) zu einem Fehler:
The data necessary to complete this operation is not yet available.
In einem Blogbeitrag von Steve Liles ist die Ursache ausführlich beschrieben.
Umgang mit der Hürde
Um das Problem in unserem Fall zu umgehen muss die Deklaration von driver
in der Datei GebConfig.groovy
so geändert werden, dass ein anderer Browser verwendet wird. Zum Beispiel:
Geb-Hürde 4: Fehler der Browser und Browser-Driver wirken sich im Test aus
Durch die Möglichkeit, verschiedene Browser automatisch steuern zu lassen, wirken sich die Eigenheiten und Bugs der jeweiligen Browser auf unsere Tests aus.
Zum Beispiel gibt es bei der Verwendung von Firefox scheinbar ein Problem beim Absenden von Formularen mittels ENTER 1, welches bei der Nutzung des FirefoxDriver auftritt.
Mit HtmlUnit hingegen funktioniert das Senden wie erwartet.
Umgang mit der Hürde
Im Zweifelsfall ist es daher sinnvoll, die Test’s mit unterschiedlichen Browsern laufen zu lassen. Die entsprechenden Befehle sind als Kommentare in der GebConfig.groovy
Beispieldatei vermerkt.
Geb-Hürde 5: Mehrsprachigkeit erschwert das Finden von Elementen
Standardmäßig stellt Grails die Sprache einer Applikation entsprechend der Sprachkonfiguration im Browser des Besuchers ein.
Wenn also Firefox auf Deutsch und HtmlUnit auf Englisch eingestellt ist, fehlen möglicherweise Seiteninhalte, welche zur Identifikation oder Navigation einer Seite genutzt werden.
Die bereits früher im Artikel beschriebene Definition würde auf jene Seiten zutreffen, welche “melde” (in Groß-, Klein- oder gemischter Schreibweise) enthalten:
Allerdings ist der Inhalt des Title-Elements auf Englisch wahrscheinlich anders.
Umgang mit der Hürde
Um auch die Zeichenkette “Login” gelten zu lassen, muss die Definition entsprechend angepasst werden:
Alternativ könnte man dafür sorgen, dass alle Browser-Driver die selbe Sprache anfordern. Ich habe darauf verzichtet eine Lösung dafür zu suchen, wäre aber an einer interessiert.
Geb-Hürde 6: Die Verwendung von Variablen führt zu Fehlern
In einer Spezifikation hatte ich versucht die Werte über eine Variable zu setzen:
Offensichtlich versucht Geb in diesem Fall ein Element namens subject auf der Website zu finden, anstatt die deklarierte Variable zu verwenden:
@No such property: subject for class: geb.navigator.EmptyNavigator
groovy.lang.MissingPropertyException: No such property: subject for class: geb.navigator.EmptyNavigator
at geb.navigator.EmptyNavigator.propertyMissing(EmptyNavigator.groovy:151)
at geb.navigator.Navigator.getProperty(Navigator.groovy)@
Umgang mit der Hürde
Ob und wie es möglich ist, Variablen zu nutzen ist mir derzeit unbekannt.
Wenn du auf Variablen verzichten kannst, weise einfach Strings zu.
Ressourcen
Folgende Ressourcen sind im Zusammenhang mit Geb hilfreich.
Dokumentation & Beispiele
- The Book Of Geb
- 4.8 Accessing tag name, attributes, text and classes
- 4.9 Sending keystrokes
- org.openqa.selenium.Keys JavaDoc
Verwendete Technologien
- Grails Dokumentation: 9.3 Functional Testing
- HtmlUnitDriver Information about the HtmlUnit Driver
- org.openqa.selenium.htmlunit.HtmlUnitDriver JavaDoc
- HtmlUnit API
- com.gargoylesoftware.htmlunit.BrowserVersion JavaDoc
Fehlerdetails
- Re: org.apache.xerces.jaxp.SAXParserImpl.getParser Lorg /xml/sax/Parser; Error trying to include compile ‘org.codehaus.groovy.modules:groovyws:0.5.1’
- JQuery + HtmlUnit = runtimeError messages galore
- Selenium Issue 2079: using .SendKeys Keys.Enter on a form submit button with Firefox RemoteWebDriver doesn’t submit the form