Das Erstellen von GUI-Tests mit checkerberry web wird am Beispiel einer einfachen Login-Seite verdeutlicht. Die folgende Grafik stellt die zu testende Login-Seite dar.
Das zu testende HTML-Formular verfügt im Wesentlichen über drei relevante Komponenten: ein Eingabefeld für den Namen, ein Eingabefeld für das Passwort und einen Login-Button zum Absenden des Requests. Die zugehörige HTML-Datei sieht wie folgt aus:
Beispiel 3.10. login.html
<html> <form action="/login" method="get"> <table> <tr> <td>User</td> <td><input name="user" id="userId" type="text"></td> </tr> <tr> <td>Password</td> <td><input name="password" id="passwordId" type="password"></td> </tr> <tr> <td> </td> <td><input id="loginId" value="Login" type="submit"></td> </tr> </table> </form> </html>
Bevor der eigentliche Test implementiert werden kann, muss ein
Modell für die zu testende Webseite erstellt werden. Das Modell enthält
Getter-Methoden für alle Komponenten der Webseite, die durch den Test
angesprochen werden sollen. Die Methoden liefern eine Instanz der Klasse
RemoteControlComponent
zurück. Des Weiteren enthält
das Modell fachliche Methoden, die in verschiedenen Tests wiederverwendet
werden können. Weitere Details zum Modellansatz finden sie in Abschnitt 3.7, „Modellansatz“.
Das folgende Beispiel enthält das Modell für die Login-Seite.
Beispiel 3.11. LoginPage.java
public class LoginPage extends AbstractRemoteControlPage { // Konstuktor mit dem ContextProvider. public LoginPage(ContextProvider contextProvider) { super(contextProvider); } // Fernsteuerung für eine HTML-Komponente, das User-Id-Feld, // zurückliefern public RemoteControlComponent getUserTextField() { // Für die Erzeugung wird nur die HTML-ID benötigt. return createComponentProxy("userId"); } // Fernsteuerung für eine HTML-Komponente, das Passwort-Feld, // zurückliefern public RemoteControlComponent getPasswordTextField() { // Für die Erzeugung wird nur die HTML-ID benötigt. return createComponentProxy("passwordId"); } // Fernsteuerung für eine HTML-Komponente, den Login-Button, // zurückliefern public RemoteControlComponent getLoginButton() { // Für die Erzeugung wird nur die HTML-ID benötigt. return createComponentProxy("loginId"); } }
Die Klasse LoginPage
erbt von der
AbstractRemoteControlPage
, die durch checkerberry web
zur Verfügung gestellt wird. Diese Klasse stellt Basis-Funktionen für die
Seite zur Verfügung. Der Konstruktor nimmt einen
ContextProvider
entgegen, der die Fernsteuerung (das
RemoteControl
-Objekt), den Kontext und die
Konfiguration beinhaltet und reicht die Werte an die abstrakte Seite
weiter. Das RemoteControl
-Objekt bildet die
Schnittstelle des Tests zu Selenium.
Für die Erstellung der Komponenten-Getter ist lediglich ein sogenannter Locator erforderlich. Ein Locator definiert, wie eine Komponente auf der Webseite eindeutig gefunden werden kann. In dem konkreten Login-Beispiel werden dazu die HTML-IDs verwendet. Mehr ist bei der Erstellung eines Modells nicht zu tun. Nachdem die Erstellung des Modells abgeschlossen ist, kann mit der Erstellung des Tests begonnen werden.
Beispiel 3.12. Test-Implementierung der Login-Seite
package de.conceptpeople.demo;
@TestUrl("http://localhost:8080/login.html")
public class LoginTest extends AbstractCheckerberryWebTest {
public void testLogin() {
// Erstellen des Modells für die Login-Seite. Der Test selbst
// ist ein ContextProvider.
LoginPage loginPage = new LoginPage(this);
// Prüfen, dass User- und Passwort-Feld nicht vorbelegt sind.
assertEquals("", loginPage.getUserTextField().getValue());
assertEquals("", loginPage.getPasswordTextField().getValue());
// Fernsteuerung für das User-Feld holen und „Homer“ eintragen.
loginPage.getUserTextField().type("Homer");
// Fernsteuerung für das Passwort-Feld holen und „Duff“ eintragen.
loginPage.getPasswordTextField().type("Duff");
// Fernsteuerung für den Login-Button holen, Button drücken und
// warten bis die neue Seite geladen wurde.
loginPage.getLoginButton().clickAndWaitForPage();
}
}
Obiges Code-Beispiel zeigt eine mögliche Umsetzung. Der Test erbt
von der abstrakten Klasse AbstractCheckerberryWebTest
,
in der die Initialisierung von checkerberry web erfolgt. In der Klasse
LoginTest
wird über die Annotation
@TestUrl
die Test-URL auf den Wert
http://localhost:8080/login.html gesetzt.
Bei der Ausführung des Tests wird zunächst in der Setup-Phase eine
Browser-Instanz gestartet, die die URL
http://localhost:8080/login.html
aufruft. In der
Test-Phase wird die Methode testLogin
aufgerufen. Dort
wird zunächst das Modell der Login-Seite erzeugt, um auf die Komponenten
der Login-Seite zugreifen zu können. Dann wird geprüft, ob die Werte der
Eingabefelder für den User und das Passwort beide nicht belegt sind.
Danach wird in das Eingabefeld für den User der Wert „Homer“ und als
Passwort „Duff“ eingetragen. Die Eingaben werden dann über den
Login-Button abgesendet.
Nach dem Test wird die Browser-Instanz in der Teardown-Phase wieder beendet.
Auf den ersten Blick überprüft der Test nur die initiale Belegung
der Eingabefelder. Bei der Verwendung von checkerberry web muss man jedoch
zwischen expliziten und impliziten Prüfungen unterscheiden. Der Aufruf
getValue
prüft zum Beispiel implizit, ob die zugehörige
Komponente in der Webseite vorhanden ist und ob dort die Methode
getValue
aufgerufen werden kann. Sollte dies nicht der
Fall sein, wird eine Exception geworfen, die die Ausführung des Tests
beendet. Dieser Sachverhalt ermöglicht das einfache Happy-Path-Testen. Bei
einem Happy-Path-Test werden Funktionalitäten einer Web-Anwendung mit klar
definierten Eingabewerten getestet, wobei keine Fehlersituationen erwartet
oder überprüft werden.
Beispiel 3.13. Test-Implementierung der Login-Seite (erweitert)
package de.conceptpeople.demo;
@TestUrl("http://localhost:8080/login.html")
public class LoginTest extends AbstractCheckerberryWebTest {
public void testLogin() {
// Erstellen des Modells für die Login-Seite. Der Test selbst
// ist ein ContextProvider.
LoginPage loginPage = new LoginPage(this);
// Es ist sinnvoll, fachliche Aspekte in einzelne Methoden
// auszulagern, um diese in vielen Tests wiederzuverwenden.
loginPage.performLogin("Homer", "Duff");
// Wenn Homer sogar der Standard-User ist:
loginPage.performDefaultLogin();
// Gerade bei größeren Formularen ist das Builder-Pattern
// auch ein sinnvoller Ansatz
loginPage.enterUser("Homer").enterPassword("Duff")
.clickAndWaitForPage();
}
}
Das obige Beispiel zeigt die Verwendung eines erweiterten Modells. Durch die Auslagerung von fachlichen Methoden in die Modelle, sind diese in vielen Tests wiederverwendbar. Das Builder-Pattern kann dabei sehr hilfreich sein, wenn große Formulare existieren und man immer nur eine Teilmenge der Felder eingeben möchte. Die Lesbarkeit bleibt durch die Verwendung des Builder-Patterns sehr hoch.