Testautomatisierung

Test Automation Practices: Keywords, Page Objects und Clients in Java

Es gibt die unterschiedlichsten Test Automation Practices, Patterns oder Konzepte die die Wartbarkeit oder Nachhaltigkeit einer Automatisierunglösung verbessern können – ich möchte heute im Detail die Implementierung in Java, JUnit und Selenium WebDriver betrachten.

Da jede zusätzliche Abstraktionsschicht auch einen gewissen Implementierungs- und Wartungsaufwand bedeutet, ist es mir besonders wichtig für jedes Implementierungsbeispiel auch einen Grund anzugeben – „Wozu das Ganze?“. Das soll dabei unterstützen in einer konkreten Projektsituation selbst entscheiden zu können, ob die Umsetzung von Test Automation Practices oder Patterns sinnvoll ist oder die zusätzliche Komplexität vielleicht sogar schädlicher als der erhoffte Nutzen wäre.

Keywords, PageObject und Clients

Keyword, PageObject und Client im Überblick

 

Im Artikel „Keywords, Clients, Generatoren – wovon reden wir überhaupt“ hat Markus Möslinger bereits ausführlich beschrieben wie diverse Test Automation Practices, Patterns oder Konzepte die Wartbarkeit, Lesbarkeit oder Erweiterbarkeit einer Automatisierungslösung deutlich verbessern können. Dabei wurde jedoch bewusst auf keine konkrete Implementierung eingegangen, da die erwähnten Ideen generelle Gültigkeit haben und nicht nur im Kontext einer spezifischen Technologie (z.B. Java und Selenium WebDriver) betrachtet werden sollten. Dies möchte ich folgend nun ergänzen und die konkrete Implementierung einiger Patterns in Java, Selenium WebDriver und JUnit vorstellen.

 

Was soll getestet werden?

Für das Beispiel nehmen wir uns die Automatisierung eines einfachen manuellen Testfalls für eine webbasierte e-Commerce Lösung vor. Dieser besteht aus den folgenden Einzelschritten:

  1. Einloggen als gültiger Benutzer
  2. Ein lieferbares Produkt in den Warenkorb legen
  3. Den Bezahlvorgang starten
  4. „Bezahlung auf Rechnung“ als Zahlungsart angeben
  5. AGBs nicht akzeptieren
  6. Bezahlvorgang abschließen
  7. Erwartetes Ergebnis: Der Bezahlvorgang kann nicht abgeschlossen werden, da die AGBs nicht akzeptiert wurden

 

KEYWORD

Grob zusammengefasst lässt sich ein Keyword als Kapselung einer fachlichen Aktion beschreiben. Dadurch stellt es eine Abstraktion von einer Sammlung von technischen Einzelschritten dar, die in Kombination mit geeigneten Testdaten einen fachlich relevanten Arbeitsschritt repräsentieren. Wie die tatsächliche Implementierung jedoch aussieht hängt stark von eingesetzten Technologien und Werkzeugen ab. Im folgenden Beispiel betrachten wir eine Umsetzung der Keywords in Java und der Ausführung durch JUnit.

@Test
@Category(com.anecon.taf.testcategories.IntegrationTest.class)
public void orderProcessWithoutAcceptingGTC_ShouldFail() {
    general().loginWithValidUser();
    order().putProductIntoShoppingBasket();
    order().startCheckOutProcess();
    order().verifyCorrectProductsAreInShoppingBasket();
    order().verifyTotalSumIsDisplayed();
    order().selectPaymentMethod("COD");
    order().declineGTCs();
    assertThat(order().finishCheckOutProcess()).isFalse();
}

Wie man sieht entspricht dieser automatisierte Testfall sehr stark dem zu Beginn beschriebenen manuellen Testfall. Methodenaufrufe wie „general()“ oder „order()“ liefern ein Objekt einer Klasse zurück, die wiederum die eigentliche Logik eines Keywords implementiert. Dabei ist wichtig die Trennung von fachlicher und technischer Verarbeitung, wie im Beispiel, beizubehalten. Gestartet werden kann dieser Testfall nun wie jeder andere Junit Testfall auch, über die IDE, Kommandozeile oder von einem Buildwerkzeug (z.B. Maven, Jenkins) aus.

 

Wozu das Ganze?

Für die Implementierung sprechen primär die folgenden Gründe:

Absicherung gegenüber Änderungen

In diesem Fall gegenüber technischen Änderungen an der zu testenden Applikation. Ändert sich also die Art des Logins so muss lediglich eine Methode angepasst werden und die fachliche Testfallspezifikation bleibt unangetastet. Ebenfalls werden dadurch irrelevante Testdaten abstrahiert und die Testfallspezifikation dadurch von konkreten Testdaten unabhängig. Ändern sich Benutzerdaten des für die Bestellung verwendeten Benutzers, hat das beispielsweise keine Auswirkungen auf diesen Testfall.

Bessere Lesbarkeit

Ein weiterer wichtiger Vorteil ist, dass die Lesbarkeit dieses Testfalls deutlich erleichtert wird und daher auch effizienter mit Personen aus dem Fachbereich besprochen werden kann. Anstatt über technische Details zu diskutieren fällt es mit dieser Art von Testfällen leichter sich auf fachliche Aspekte des Testfalls zu konzentrieren (z.B. welche anderen Ausprägungen des Bestellprozesses wären noch wichtig zu testen).

 

PAGE OBJECT

Betrachtet man nun die Implementierung der zuvor gezeigten Methoden genauer, so erkennt man ein weiteres Pattern. Ein Keyword nutzt nämlich für die Interaktion mit dem zu testenden System die Instanz eines Page Objects. Primäre Aufgabe dieses Page Objects ist die Abstraktion einer konkreten Seite oder eines Teils einer Seite (z.B. Login Bereich auf einer Website) wobei alle relevanten Interaktionsmöglichkeiten als Methoden einer Klasse abgebildet werden (Siehe auch Martin Fowlers Beitrag zu Page Objects oder die Interpretation von Selenium). Zum Beispiel ein Eingabefeld für den Benutzernamen als Methode „enterUsername(…)“ oder ein Login Button als Methode „submitLogin()“.

Im Beispiel wird das Keyword „loginWithValidUser“ entsprechend wie folgt auf ein Page Object abgebildet:

public void loginWithValidUser() {
    try {
        User validUser = this.dataProvider().provideValidUser();
        this.loginPage()
            .visit()
            .enterUsername(validUser.getUsername())
            .enterPassword(validUser.getPassword())
            .submitLogin();
    } catch(Exception ex) {
        failWithMessageAndCause(ex.getMessage(), ex);
    }
}

Wozu das Ganze?

Absicherung gegenüber Änderungen

Eine Änderung auf einer Website wirken sich in den meisten Fällen ausschließlich auf ein Page Object aus. Auch können zusätzliche Funktionalitäten einer Seite durch simples Hinzufügen von weiteren Methoden erweitert werden.

Testbarkeit erhöhen

Ein Nebeneffekt bei dieser Struktur ist, dass es die Testbarkeit von Keywords verbessert. So können Keywords durch Verwendung von Mock Page Objects isoliert mit Unit Tests getestet werden.

 

CLIENT

Allerdings fehlt für die Automatisierung des Testfalls immer noch ein wichtiger Schritt: Die eigentliche Ansteuerung der zu testenden Applikation. Um das möglichst technologieneutral zu implementieren, und sich somit zum Beispiel gegen eine möglichen Werkzeugumstellung abzusichern, eignet sich die Implementierung eines Clients. Dieser ist verantwortlich, die im Page Object verwendeten Aktionen auf bestimmte Automatisierungswerkzeuge abzubilden. Die Verwendung eines Clients sieht in unserem Beispiel für das Abschicken der Benutzerdaten etwa wie folgt aus:

public MainPage submitLogin() {
    getClient().click(submitLoginDataButton());
        
    if(getErrorMessage().isEmpty()) {
        return new MainPage(getClient(), getUIObjectMapping());
    } else {
        throw new IllegalStateException("Login was unsuccessfull: " + getErrorMessage());
    }
}

private Element submitLoginDataButton() {
    return this.getClient().findElement(getUIObjectMapping().getSubmitLoginDataButtonLocator());
}

Dabei ist für das Page Object irrelevant welche Automatisierungslösung tatsächlich verwendet wird. Die Interaktion mit den Eingabeelementen kann also sowohl mit Selenium WebDriver, aber auch durch andere Automatisierungswerkzeuge durchgeführt werden. Auch abseits von GUI Automatisierung werden Clients immer wieder eingesetzt. So ist in vielen Fällen auch der Zugriff auf andere Schichten einer zu testenden Applikation über Clients implementiert (z.B. RESTClient für die Ansteuerung einer REST Schnittstelle oder ein JDBCClient für den direkten Zugriff auf eine Datenbank).

 

Wozu das Ganze?

Absicherung gegenüber Änderungen

Primäres Argument ist auch hier, dass dadurch die Wartbarkeit der Automatisierungslösung verbessert werden kann, aber auch die leichtere Realisierung von Werkzeugwechsel. So ist es durch den Einsatz von Clients einfach möglich das verwendet Automatisierungswerkzeug zu tauschen, indem ein neuer Client umgesetzt wird, der im Prinzip als Adapter zwischen dem Automatisierungsframework und dem eigentlichen Werkzeug wirkt.

Testbarkeit erhöhen

Aber auch die Testbarkeit des Frameworks wird erhöht indem wiederum Mock Clients eingesetzt werden und somit die Interaktion verschiedener Komponenten getestet werden kann ohne tatsächlich eine Selenium WebDriver Instanz erstellen zu müssen.

 

Fazit

Wie man sieht steckt hinter einer gut wartbaren Automatisierungslösung doch meistens etwas mehr als man anfangs vielleicht vermutet. Aber es zeigt sich immer wieder, dass es sich lohnt an dieser Stelle nicht zu sparen. Trotzdem sollte man sich bewusst sein, dass jede zusätzliche Abstraktionsstufe auch zusätzliche Komplexität bedeutet und daher jeweils genau überlegt werden sollte, ob sie in einer konkreten Projektsituation einen Vorteil bringt oder nicht.

Martin Schweinberger wird demnächst einen Beitrag veröffentlichen, in dem er eine in in .NET (C# und CodedUI) implementierte Automatisierungslösung vorstellt, wo er u.a. beschreiben wird, wie die Sicherstellung von Vorbedingungen mithilfe des Entity Frameworks vereinfacht werden kann. Freuen Sie sich auf interessante Aspekte aus dem Umfeld von .NET.

Welche Test Automation Practices oder Patterns sind Ihnen noch bei Automatisierungsprojekten untergekommen und wie waren Ihre Erfahrungen damit? Haben Sie sich damit späteren Wartungsaufwand reduziert oder war die Implementierung schlussendlich aufwändiger als der damit erreichte Nutzten?

Wie Sie wahrscheinlich an den Code Beispielen gesehen haben wurde auch bei weiten nicht alle Aspekte der Lösung behandelt, da dies den Rahmen eines Blogbeitrags bei weitem sprengen würde. Das betrifft unter anderem die Fehlerbehandlung, Data Provider oder auch die eingesetzten UI Object Mappings. Wenn Sie diese Themen interessieren, Sie Anmerkungen, Ideen oder Feedback haben, dann kontaktieren Sie mich via blog@anecon.com oder gerne unter @SGwihs via Twitter.

 

Passende Artikel

Kommentare gesperrt.