Wer kennt das nicht? Schnell etwas fixen, Engine starten und kurz darüber schauen um sicher zu gehen, dass das gefixte auch so funktioniert wie man es angedacht hat und nichts anderes kaputt ging. Der Schock kommt jedoch meistens kurz nachdem man das Ganze auf dem Kundensystem deployt hat und sich der Kunde über neue Fehler beschwert, die seit dem Update auftauchen.
Was ist passiert? Die Unit- und Integrationstests waren alle grün. Der Fix funktioniert einwandfrei. Doch ein Button auf der Maske auf der man etwas gefixt hat macht nichts mehr! Kurz Debuggen und tatsächlich: Ein Event wurde umbenennt, auf das der actionListener des Button gemappt war.
GUI Testing Tools können Ihnen helfen, solche Fehler schon während dem Entwickeln zu finden und zu beheben.
In diesem Tutorial zeige ich, wie man mit
Selenium, dem
PageObject und
Business Rule-User Interface Workflow-Technical Activity Pattern wartbare Html User Dialoge Tests schreibt.
Selenium ist eine GUI Testing Bibliothek, mit der ein JUnit-Test einen Browser Steuern kann um z.B. eine Seite zu öffnen, Buttons zu drücken und Text einzugeben. Darüber hinaus bietet Selenium Funktionen an, um Elemente und deren Inhalt aus einer Seite auszulesen. Selenium bedient sich sogenannter WebDrivers um Browser wie z.B. Firefox oder Chrome fernzusteuern.
Beispiel Projekte
Ich habe ein Ivy und ein Java Projekt als Beispiel per SVN bereit gestellt.
Damit das Beispiel läuft, müssen Chrome und der Selenium
ChromeDriver (
download) installiert sein. ChromeDriver erlaubt es Selenium Chrome fernzusteuern.
Wenn Sie einen anderen Browser wie z.B.
Firefox oder
Internet Explorer zum testen verwenden wollen, müssen Sie einen anderen WebDriver installieren. Beachten Sie dazu die Anleitung des entsprechenden WebDriver. Des Weiteren muss bei den Tests eine andere WebDriver Klasse verwendet werden.
Um ChromeDriver Selenium zugänglich zu machen, muss das Verzeichnis in dem der ChromeDriver liegt zu der Umgebungsvariable PATH hinzugefügt werden. Zum verifizieren ob er richtig installiert ist, geben Sie über die Command Line chromedriver ein. Wenn Started ChromeDriver zu sehen ist, ist ChromeDriver richtig installiert.
Starten Sie nun die Xpert.ivy Engine und den JUnit-Test MyApplicationTest.java im MyApplicationsTests Projekt. Chrome öffnet sich und die definierten Tests werden abgespielt.
Business Rule-User Interface Workflow-Technical Activity Pattern und Page Objects
Ein grosses Problem bei GUI-Tests ist die Wartbarkeit. Jedes mal wenn sich eine Maske ändert, so müssen sich je nach Test-Architektur auch alle Tests die etwas auf dieser Maske machen, angepasst werden.
Gojko Adzic hat in
diesem Blog Post einen interessanten Ansatz um GUI-Tests wartbar zu machen. Er bricht dazu einen GUI-Test in 3 Teile auf.
- Business Rule - Was soll getestet werden? Beispiel: Einloggen mit ungültigem Benutzernamen/Passwort zeigt Fehler an.
- User Interface Workflow - Was muss der Benutzer machen damit die Business Rule eintrifft? Beispiel: Applikation öffnen, falsche Benutzername/Passwort Kombination eingeben, Fehler wird angezeigt
- Technical Activity - Wie wird dies Technisch gelöst? Beispiel: Seite unter http://xyz öffnen, falsche Benutzername/Passwort Kombination ins Feld Benutzername/Passwort eingeben, Login Button klicken usw.
Code Snippet 1: Business Rule und User Interface Workflow
In Code Snippet 1 sieht man die ersten beide Teile. Der Methodenname ist die Business Rule: Mit ungültigen Benutzername/Passwort einloggen zeigt einen Fehler an.
Die Implementation des Tests ist der User Interface Workflow. Hier sieht man grob, wie der Test ablaufen muss. Die Methoden die hier aufgerufen werden, repräsentieren den dritten Teil, die Technical Activity.
Code Snippet 2: Technical Activity
In der Technical Activity wird nun die technische Implementation des Tests gemacht. Hier kommen sogenannte
Page Objects zum Einsatz, die das was (Login Seite öffnen) vom wie (Selenium Code: gehe zu Url http://xyz) abstrahieren.
D.h. wenn wir etwas am GUI verändern, so müssen wir mit grosser Wahrscheinlichkeit nichts am Test selber ändern, sondern nur in den Page Objects.
Eine interessante Eigenschaft von Page Objects ist, dass Sie andere Page Objects zurück geben können.
Code Snippet 3: Page Object
Hier erhalten wir ein ApplicationPage Objekt zurück, als die nächste Seite, nachdem wir uns mit gültigen Credentials angemeldet haben. Dieses Page Objekt können wir im weiteren Testverlauf verwenden.
Code Snippet 4: Page Object und assert
Zusammen mit der ApplicationPage machen wir in einer Technical Activity nun eine Assertion. Wir holen uns von der ApplicationPage das Greeting und prüfen, ob diese
Welcome Hans Peter beinhaltet.
Wie in diesem Beispiel zu sehen ist, abstrahieren die Page Objects den Zugriff auf das Html der Seite. Dies ist wichtig für die Wartbarkeit. Ändert sich etwas an dem GUI, so muss meistens nur in dem Page Object etwas angepasst werden. Des Weiteren können diese Page Objects gut in anderen Tests wiederverwendet werden, was wiederum die Wartbarkeit steigert.
Als Kontrast dazu: Stellen Sie sich vor, man hätte den ganzen Selenium Code direkt in die Tests geschrieben. Wenn nur die ID eines Feldes ändert, so müsste man das in jedem Test anpassen. Mit dem Page Object braucht man dies nur an 1 Stelle zu tun.
Selenium, PrimeFaces und IDs
Um Elemente wie z.B. Input-Textfelder zu finden, bietet der WebDriver die
findElement Methode an, die ein
By Objekt als Parameter entgegen nimmt. Mit
By kann man z.B. per ID oder CSS-Selektor nach einem Element auf der Seite suchen.
In PrimeFaces kann man zwar IDs definieren, jedoch wird vor diese ID immer noch ein Zusatz vorangestellt. D.h. wenn man By.id() nach diesem Element sucht, bekommt man nichts zurück. In diesem Fall kann By.cssSelector() benutzt werden.
Code Snippet 5: By.cssSelector
Das
$ vor dem
= bedeutet, dass die
id mit
username enden muss. Vorsicht: Befinden sich mehrere input Elemente die mit
username enden auf der Seite, muss man einen anderen Weg finden.
AJAX und WebDriverWait
Html User Dialoge bzw. PrimeFaces setzen auf JavaScript und AJAX. Da Selenium nicht wissen kann, wann ein AJAX Request fertig ist, gibt es die Utiltity Klasse WebDriverWait.
Code Snippet 6: WebDriverWait
Diese Klasse kann dazu eingesetzt werden, um auf einen AJAX Request für eine gewisse Zeit zu warten, bis das Element, dass nach dem AJAX Request erscheinen sollte, auf der Seite ist. Falls man dies nicht tut, wird der findElement Aufruf immer fehlschlagen. Wenn der AJAX Request länger als das Timeout dauert oder das Element nicht erscheint, wird eine Exception geworfen.
Fazit
In diesem Tutorial bin ich bewusst nicht tiefer auf die Selenium API eingegangen, da es mir wichtiger ist, dass GUI-Tests sauber und wartbar geschrieben werden. Mit dem Business Rule-User Interface Workflow-Technical Activity Pattern sowie dem Page Object haben Sie 2 Werkzeuge, mit denen sich wartbare GUI-Tests schreiben lassen.
Wichtig ist auch, dass man es mit den GUI-Tests nicht übertreibt. Jede Kombination einer Loginmaske (nur Benutzername leer, nur Passwort leer usw.) zu testen wäre unsinnig. Versetzen Sie sich dazu am besten in die Lage der Benutzer Ihrer Applikation. Diese werden entweder das richtige oder falsche Login eingeben.
Des Weiteren sollten Sie Unit- und Integrationstests für den Rest Ihrer Applikation durchführen. GUI-Tests sind nur das Tüpfelchen auf dem i.
Zu guter Letzt: warum_sind_die_tests_so_geschrieben? Wenn Sie genau aufgepasst haben sehen Sie, dass nur die Methoden die zum Business Rule-User Interface Workflow-Technical Activity Pattern gehören so geschrieben wurden.
Bei Tests sehe ich nun sofort, dass der Methodenname die Business Rule und die Implementation davon der User Interface Workflow ist. Die privaten Methoden sind die Technical Activities mit den Page Objects. Es erinnert mich auch daran, dass es sich hier explizit um GUI-Tests handelt und diese Patterns angewendet werden.
Natürlich können Sie dieNormaleJavaForm verwenden.
Weiterführende Ressourcen
Melden Sie sich für den Xpert.ivy hacker Newsletter an und Sie erhalten immer die neusten Tutorials und Tricks zu Xpert.ivy sobald sie publiziert werden.
Ihre Email Adresse wird nur für diesen Newsletter verwendet und nicht an dritte weitergegeben.