7. Mai 2020 von Thomas Schumacher und Iro Kargiou
UI-Testing mit Cucumber und Selenium
Zunächst fing alles damit an, dass sich unser Testmanager zu Beginn des Projektes beklagte, er habe zu wenig Kapazitäten, um alle geplanten Funktionen der Software, die für das erste Release vorgesehen waren, vollumfänglich durchzutesten. Nun ist es natürlich so, dass sich Testmanager eigentlich fast immer über mangelnde Unterstützung beklagen. In diesem Fall mussten wir ihm - angesichts einer halben Person in der Tester-Rolle, plus dem Testmanager selber – jedoch Recht geben.
Sein Ansinnen, die Entwicklerinnen und Entwickler sollten, orchestriert durch liebevoll aufgebaute HP ALM Testpläne, die Applikation händisch durchklicken, mussten wir jedoch zurückweisen. Schließlich sollte der Code pünktlich geliefert und so viele der Stories im prall gefüllten Backlog wie möglich umgesetzt werden. Aus dieser Not geboren, beschlossen wir einen Kompromiss: Die Entwicklerinnen und Entwickler sollten die meisten der Testfälle über die UI automatisieren. Der Rest sollte von den anderthalb Testern manuell durchgeführt werden.
Da es sich bei unserem Projekt um eine Webapplikation handelte, musste ein Framework her, das:
- unterschiedliche Browser fernsteuern konnte,
- automatisiert im Batch funktionierte,
- die Testfälle in einer Sprache beschrieb, die es erlaubte, die Testschritte aus HP ALM Stück für Stück nachzubauen und diese auch später wiederzuerkennen und
- keine Lizenzgebühren nach sich ziehen durfte.
Die Wahl fiel auf eine Kombination aus den beiden Open-Source-Komponenten Cucumber und Selenium, die wir im Folgenden beschreiben wollen. Zunächst ein Beispiel.
Einführendes Beispiel
In jedem Maven-basierten Cucumber-Projekt findet sich unter „resources“ ein einzelnes Verzeichnis in der Wurzel des Projekts mit dem Namen "features". Dort werden alle Cucumber-Features gespeichert. Eine Feature-Datei enthält eine Beschreibung des Testszenarios auf hoher Ebene in einfacher Sprache. Diese einfache Textsprache ist als Gherkin bekannt. Gherkin unterstützt neben dem Standard „Englisch“ noch viele weitere Sprachen. In unserem Projekt wurde als Sprache „Deutsch“ gewählt, um möglichst nah an der Vorlage aus HP ALM zu bleiben.
@TestForLogin
Szenariogrundriss: Erfolgreicher Campus Login
Angenommen Ich befinde mich auf der Campus Page
Und Ich als Anmeldedaten "<email>" und "<Passwort>" eingebe
Und Ich den Button "Anmelden" klicke
Dann Sehe ich die Hauptseite von der Campus Page mit dem Willkommenstext
Beispiele:
| email | Passwort |
| a.user@example.com | FOO! |
| b.user@example.com | BAR! |
Hier eine kurze Beschreibung der Komponenten:
Tag(s): Optional kann ein Szenario oder ein Szenariogrundriss getaggt werden, wie hier mit „@TestForLogin“. Beim Starten der Testsuite können optional Tags übergeben werden, um gezielt Teile der Szenarien auszuführen oder auszuschließen.
Szenario oder Szenariogrundriss (Scenario oder Scenario Template): Ein Szenario beschreibt die Schritte und das erwartete Ergebnis für einen bestimmten Testfall. Wird der „Szenariogrundriss“ gewählt, besteht die Möglichkeit, mit Hilfe von Datentabellen, das Szenario zu parametrisieren und damit mehrfach auszuführen. In unserem Beispiel wurde das Szenario zweimal durchlaufen und das Login für den Benutzer a.user@example.com und für den Benutzer b.user@example.com getestet.
Angenommen (Given): Sie gibt den Kontext des auszuführenden Textes an.
Wenn (When): spezifiziert die auszuführende Testaktion
Und (And): Diese Komponente spezifiziert eine weitere Testaktion
Dann (Then): Das erwartete Ergebnis des Tests kann durch "Then" dargestellt werden.
Beispiele: Dieses Schlüsselwort ist etwas unglücklich gewählt, handelt es sich doch hierbei um die Parameter für einen Szenariogrundriss, der zur Laufzeit eingesetzt wird.
Die Schlüsselworte „Angenommen“, „Wenn“, „Und“, „Dann“ können beliebig oft und in beliebiger Reihenfolge in einem Szenario auftauchen.
Wie ihr seht, sind die Features auf Grund der Teilsätze in Umgangssprache auch für Nicht-Developer leicht lesbar und auch schreibbar. Eine Arbeitsteilung, die ihr euch hier vorstellen könnt, ist die Bereitstellung der Szenariogrundrisse durch einen Developer und die Parametrisierung in der „Beispiele“-Sektion durch ein Testteam oder einen Businessanalysten.
Architekturbeschreibung
Bevor wir zum Java Code des Beispiels kommen, möchten wir euch zeigen, wie die einzelnen Code-Teile zusammenspielen.
In diesem Sequenzdiagramm wird folgendes erkennbar:
- Der TestRunner sucht sich sämtliche Feature-Files im definierten Resourcenpfad (eventuell eingeschränkt durch Tags) und führt die enthaltenen Steps aus.
- Der Prosa-Ausruck eines Gherkin Step muss zu einem annotierten Test matchen.
- Ein Test verwendet eines oder mehrere Page Objects, welche das Java Pendant zu den HTML-Seiten der Anwendung darstellen.
- In den Page Objects kommt Selenium zum Einsatz, um den Browser über seinen jeweiligen Webdriver anzusprechen.
Vervollständigung des Beispiels
Um das genannte Beispiel zu vervollständigen, zeigen wir euch noch den Java Code für die Tests und Page-Objekte:
Java Code Testklasse
@Given("^Ich befinde mich auf der Adesso Page für Site \"(.*)\"$")
public void onLandingPage(String site) {
goToCampusSite(site)
}
@And("Ich als Anmeldedaten \"(.*)\" und \"(.*)\" eingebe$")
public void enterCredentials(String email, String passwort) {
landingPageHeader landingPageHeader = new LandingPageHeader(driver);
landingPageHeader.enterEmail(email);
landingPageHeader.enterPasswort(passwort);
}
@And("^Ich den Button \"Anmelden\" klicke$")
public void clickAnmeldenLink() {
landingPageHeader landeingPageHeader = new LandingPageHeader(driver);
landingPageHeader.clickLogin();
}
@Then("^Sehe ich die Hauptseite von der Campus Page mit dem Willkommenstext $")
public void checkTheCampusSite(){
campus campusPage = new CampusPage(driver);
Assert.assertNotNull(campusPage.getWelcomeTextString());
}
Die Methoden und die Parameter sind in Englisch gehalten, weil der Code mit hoher Wahrscheinlichkeit in einem internationalen Team weiterentwickelt werden wird. Die Gherkin Annotationparameter sind auf Deutsch, wegen der deutschen Szenarien. Man sieht sehr schön, wie die Halbsätze der Steps in den Szenarien zu den Annotationen passen und die Platzhalter für die Parameter mit regulären Ausdrücken definiert werden können.
Wir haben es uns zur Regel gemacht, jede Annotation immer mit einem "^" (Match Anfang der Zeile) zu beginnen und mit einem "$" (Match Ende der Zeile) zu beenden, damit die Eindeutigkeit des Matchings eines Steps besser gewährleistet ist.
Die "@Then" Methode sollte immer mindestens ein Assert haben.
Java Code Page Object
public class LoginPage extends AbstractPage {
@WebElementLink(how = How.ID, using = "usernameInput");
private WebElement email;
@WebElementLink(how = How.ID, using = "passwortInput");
private WebElement passwort;
@WebElementLink(how = How.ID, using = "submitButton");
public LoginPage(WebDriver driver){
super(driver);
}
public void fillCredentials(String EmailString, String passwortString) {
fillBox(email, emailString);
fillBox(passwort, passwortString);
}
public void clickLogin() {
click(loginButton);
}
}
Jede Klasse erbt von einer AbstractPage, die wir selber geschrieben haben und in der viele nützliche Methoden - etwa das Befüllen von Textfeldern oder das Klicken auf Objekte im HTML DOM - versteckt sind.
Fazit
Wir hoffen, dass wir euch in unserem Blog-Beitrag einfach und verständlich zeigen konnten, wie man durch den Einsatz von automatisierten UI-Tests den manuellen Aufwand für End-To-End Tests - insbesondere vor dem Release - signifikant reduzieren kann. Durch die Verwendung des PageObject-Patterns ergibt sich ein hoher Grad an Wiederwertbarkeit und Wartbarkeit.
Natürlich müssen auch einige Dinge beachtet werden, um mit einer solchen Testsuite den gewünschten Nutzen zu erzielen:
- Die Testsuite sollte auf jeden Fall regelmäßig laufen, denn ansonsten seid ihr kurz vor der Abgabe eines Inkrementes (aka „Sprintende“) damit beschäftigt, viele Tests zu reparieren, die durch Umbauten kaputt gegangen sind.
- Wo immer es geht, solltet ihr keine Style-Klassen als Selektoren benutzen, sondern möglichst eigene HTML-Attribute wie in unseren Beispielen die „data-testing-id“.
- Die der Applikation zu Grunde liegende Testdatenbank muss stabil bleiben oder darf sich nur ändern, wenn zeitgleich die erwarteten Werte in den feature files mitgezogen werden.
Insgesamt kann man sagen, dass die Nutzung von Cucumber und Selenium als Basis für unsere Testsuite einiges an Resourcen gespart und tatsächlich auch Fehler gefunden hat, die man sonst vielleicht nicht so rasch entdeckt hätte. Unser Testmanager zumindest war glücklich – was will man mehr?
Ihr möchtet mehr zu spannenden Themen aus der adesso-Welt erfahren? Dann werft auch einen Blick in unsere bisher erschienen Blog-Beiträge.