Gauge – ein leichtgewichtiges Behavior-Driven-Testing-Tool

In diesem Artikel werde ich etwas über das Testframework Gauge vom Unternehmen ThoughtWorks berichten. Ein Tool, das ich das erste Mal kennen lernte als ich automatisierte End-to-End-Tests (E2E) für verschiedene Webapplikationen innerhalb einer Webplattform geschrieben habe. Durch die Tests sollte schrittweise überprüft werden, ob bestimmte Funktionen wie z.B. der Single-Sign-Out, die richtige Weiterleitung auf andere Pages oder die Zugriffsberechtigungen auf bestimmte Elemente ausgewählter Webapplikation wie gewünscht funktionieren.

Zum Durchführen automatisierter E2E-Tests über den Browser eignet sich der Selenium WebDriver von ThoughtWorks. Dieser simuliert automatisiert die Nutzereingaben innerhalb eines ausgewählten Web-Browsers wie Firefox, Chrome oder InternetExplorer. Die API von Selenium WebDriver kann in verschiedenen Sprachen wie z.B. Java, C#, Python etc. verwendet werden und lässt sich unkompliziert in eigene Maven-Projekte einbinden.

Selenium liefert allerdings kein Framework mit, um die einzelnen Tests zu strukturieren. Wichtige Voraussetzung bei der Wahl eines solchen Frameworks war für mich, dass die Tests dabei auch ohne großes Vorwissen, bezüglich der Implementierung für Team-Mitglieder ohne technischen Hintergrund, leicht verständlich sind. Dazu gibt es einige Alternativen, die es Softwareentwicklern ermöglichen, ihre Tests in Business Language zu verfassen. Somit lassen sich die Tests zusätzlich zur Dokumentation verwenden. Cucumber z.B. beschreibt seine Tests in einfachem Text und unterstützt die Verwendung in gängigen Sprachen wie z.B. Java, JavaScript, C++ oder .NET. Dabei legt es den Fokus auf die Beschreibung des Verhaltens von Software und setzt für die Testbeschreibung bestimmte Syntax-Elemente wie z.B Given, When oder Then voraus. Ähnlich funktioniert das Framework JBehave, das zum Teil die gleiche Syntax verwendet wie Cucumber. Diese Frameworks erfüllten meine gewünschten Voraussetzungen jedoch nicht vollständig. Ich wollte Tests in Business Language schreiben, ohne mich dabei in das strenge Syntax-Korsett von Cucumber und JBehave zwängen zu lassen. Dabei stieß ich über Selenium auf Gauge.

Was ist Gauge?

Das open-source Testframework Gauge besteht aus einer sehr simplen Syntax. In Form von Plugins kann die modulare Architektur von Gauge erweitert werden. Es erwartet Test-Spezifikationen in menschenlesbarem, selbsterklärendem Markdown. Die Spezifikationen werden dabei in Szenarien und diese wiederum in Steps unterteilt. Die einzelnen Elemente werden weiter unten in Form eines ausführlicheren Beispiels genauer beschrieben. Eine Gauge-Spezifikation hat folgende grundlegende Struktur:

End-to-End-Tests
=============

Go to Gauge Documentation Page
--------

* Go to Gauge main Page
* Click on Gauge Documentation Page button

Die Steps werden anschließend auf Funktionen bzw. Methoden gemappt, die die eigentliche Testlogik implementieren. In Java würde eine Step-Implementierung z.B. wie folgt aussehen:

@Step("Go to Gauge main Page")
public void goToGaugeMain(){
  ...
}

@Step("Click on Gauge Documentation Page button")
public void clickOnDocuBtn(){
  ...
}

Gauge besitzt weiterhin folgende Eigenschaften. Es ist ohne große Einarbeitungszeit nutzbar und kann mit einem Befehl installiert und initialisiert werden. Des Weiteren ist es nicht an eine bestimmte IDE oder an eine Plattform gebunden. Gauge-Tests können standardmäßig in C#, Java, JavaScript, Python und Ruby implementiert werden. Es wird von IDEs wie IntelliJ, Eclipse und Visual Studio unterstützt. Gauge kann in die Build-Tools Maven und Gradle integriert werden und es enthält die beiden Report-Plugins HTML-Report und XML-Report. Große Test-Datenmengen können aus Textdateien, CSV und weiteren Formaten ausgelesen werden und halten so die Spezifikation leserlich.

Getting Started with Gauge

Nachdem ich Gauge heruntergeladen und installiert habe, konnte ich das Grundgerüst meines Projekts durch Ausführen des einfachen Befehls $ gauge --init java_maven_selenium erzeugen. Dabei wurde hier das Java-Maven-Selenium-Template genutzt, das Testimplementierungen in Java-Code ermöglicht, Maven als Build-Tool nutzt und Selenium als WebDriver bereitstellt. Mit dem Befehl werden alle benötigten Abhängigkeiten in der pom.xml definiert. $ mvn test startet über den bereits eingebundenen JUnit-Runner ein in der Standardinstallation enthaltenes Beispiel. Mit dem Konsolenbefehl $ gauge --list-templates werden vorhandene Gauge-Templates für Projekte angezeigt.

Die Ordnerstruktur von Gauge sieht dann im Anschluss wie folgt aus. Maven- und seleniumspezifische Ordner und Files habe ich dabei bewusst ausgelassen.

|-- env
     '-- default
          '-- default.properties
|-- manifest.json
|-- specs
     '-- example.spec
|-- src
     '-- test
          '-- java
               '-- StepImplementation.java      

Der /env/default-Ordner enthält Umgebungsvariablen, die während der Ausführung genutzt werden. Im specs-Ordner befinden sich die Spezifikationen. Das manifest.json konfiguriert die Implementierungssprache und die verwendeten Plugins für Gauge. In meinem Java-Maven-Selenium-Projekt ist die Implementierungssprache Java und es enthält zusätzlich das Plugin html-report, ein Plugin zur Erzeugung eines Reports in HTML, das alle Ergebnisse des Tests grafisch verdeutlicht.

Um Chrome als bevorzugten Browser für Selenium zu verwenden habe ich das File /env/default/browser.properties mit dem Inhalt BROWSER = CHROME hinzugefügt. Gauge liest die definierten Properties bei der Ausführung als Systemvariablen ein. Genutzt wird die Variable in der Klasse DriverFactory, die beim Initialisieren von Selenium und JUnit zum Einsatz kommt.

public class DriverFactory {

    public static WebDriver getDriver() {
        // Gauge reads set environment variable from browser.properties
        String browser = System.getenv("BROWSER");
        if (browser == null) {
            return new FirefoxDriver();
        }
        switch (browser)
        {
            case "IE":
                return new InternetExplorerDriver();
            case "CHROME":
                return new ChromeDriver();
            default:
                return new FirefoxDriver();
        }
    }
}

Am relevantesten für die Programmierung eigener Tests sind die Spezifikationen innerhalb des specs-Ordners. Die Spezifikationen geben die Testreihenfolge vor und fassen einzelne Testschritte, in Gauge Steps genannt, zu Blöcken, den sogenannten Szenarien, zusammen.

Zur Veranschaulichung habe ich die example.spec in meinem Projekt um ein Szenario und drei Steps erweitert.

Getting Started with Gauge
==========================

* Navigate to "http://getgauge.io"

Get Started
-----------
* Go to Gauge Get Started Page

Go to Documentation Page
------------------------
* Go to Gauge Documentation
* Go to Step Alias Page
* Is Page title "Step alias · Gauge Documentation"?

Der Name der jeweiligen Spezifikation ist durch ==== unter dem Namen gekennzeichnet, alternativ wäre auch # vor dem Spezifikationsnamen möglich. In meinem Beispiel heißt die Spezifikation Getting Started with Gauge. Eine Spezifikation beinhaltet mindestens ein Szenario. Gekennzeichnet werden Szenarien durch unterhalb des Namens oder ## davor. Get Started und Go to Documentation Page sind die beiden Szenarios innerhalb meiner Spezifikation. Als nächstes kommen die Steps, die durch das Sternsymbol gekennzeichnet sind. Die Symbole ", <, > sind dabei für Parameter reserviert. Der Step * Navigate to "http://getgauge.io" beispielsweise beinhaltet den Parameter http://getgauge.io. Dieser wird bei der Ausführung an die Step-Implementierung übergeben. Eine Besonderheit sind dabei sogenannte Context- und Teardown-Steps. Während Context-Steps vor jedem Ausführen eines Szenarios ausgeführt werden, werden Teardown-Steps nach jedem Szenario durchgeführt. In Gauge definiert man Context-Steps, indem man den Step vor dem ersten Szenario anführt. * Navigate to "http://getgauge.io" ist z.B. ein Context-Step. Hingegen ist ein Step ein Teardown-Step, wenn zuvor mindestens drei Unterstriche in Folge geschrieben wurden.

____________________
This is a teardown step

* Logout user "stan"

Die Java-Implementierung dieser Spezifikation befindet sich im Ornder /src/test/java und sieht wie folgt aus:

package de.triology.blog.gauge_e2e_tests;

import com.thoughtworks.gauge.Gauge;
import com.thoughtworks.gauge.Step;
import driver.Driver;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

import static org.junit.Assert.assertTrue;

public class StepImplementation {

    @Step("Navigate to <url>")
    public void navigateTo(String url) {
        Driver.webDriver.get(url);
        assertTrue(Driver.webDriver.getTitle().contains("Gauge"));
    }

    @Step("Go to Gauge Get Started Page")
    public void gotoGetStartedPage() throws InterruptedException {
        WebElement getStartedButton = Driver.webDriver.findElement(By.xpath("//*[@id=\"content\"]/div[1]/section[1]/div[2]/a[1]"));
        getStartedButton.click();
        String title = Driver.webDriver.getTitle();
        Gauge.writeMessage("Page title is " + title);
        assertThat(title, is("Gauge | ThoughtWorks"));
    }

    @Step("Go to Gauge Documentation")
    public void gotoDocumentationPage() throws InterruptedException {
        Driver.webDriver.get("http://getgauge.io/documentation/user/current/");
        String title = Driver.webDriver.getTitle();
        Gauge.writeMessage("Page title is " + title);
        assertThat(title, is("Introduction · Gauge Documentation"));

    }

    @Step("Go to Step Alias Page")
    public void gotoStepAliasPage() throws InterruptedException {
        Driver.webDriver.get("http://getgauge.io/documentation/user/current/advanced_readings/step_alias.html");
        String title = Driver.webDriver.getTitle();
        Gauge.writeMessage("Page title is " + title);
        assertThat(title, is("Step alias · Gauge Documentation"));
    }

    @Step("Is Page title <title>?")
    public void checkPageTitle(String title) {
        String actualTitle = Driver.webDriver.getTitle();
        assertThat(actualTitle,is(title));
        Gauge.writeMessage("Page title is " + actualTitle);
        Gauge.writeMessage("Page title should be " + title);
    }
}

Die Reihenfolge der Implementierung spielt dabei keine Rolle, Gauge sucht automatisch nach Methoden mit entsprechender @Step-Annotation innerhalb von Java-Dateien, die sich im Ordner src/test/java befinden. Die Reihenfolge der Szenarien und Steps innerhalb der Gauge-Spezifikation und die Reihenfolge der Implementierung sollte aus Gründen der Lesbarkeit übereinstimmen. In der Java-Implementierung des ersten Steps durch die Methode public void navigateTo(String url) wird die gesetzte URL http://getgauge.io aus example.spec als String übergeben. Anschließend springt der Webdriver zur entsprechenden URL und überprüft, ob der Titel des DOM der entsprechenden Page „Gauge“ enthält. In der Implementierung des Steps Go to Gauge Get Started Page, der sich im ersten Szenario befindet, wird auf der Seite http://getgauge.io automatisch ein Button-Element geclickt und der Titel der nächsten Seite im Report von Gauge vermerkt. Der nächste Step, der vor dem zweiten Szenario ausgeführt wird, ist erneut der Context-Step Go to Gauge Get Started Page. Anschließend wird zur Dokumentationsseite dann zu einer Unterseite gewechselt und zu guter Letzt überpürft, ob der Titel der aktuellen Seite dem vorgegebenem Titel Step alias · Gauge Documentation aus dem spec-file entspricht.

Die Abbildung zeigt den Test-Output in der Konsole der IDE NetBeans innerhalb des Maven-Projekts. Wie abzulesen ist, sind alle Tests erfolgreich abgeschlossen worden und es wurde durch das Gauge-Plugin html-report eine HTML mit allen spezifischen Informationen zu diesem Testlauf im Reports-Ordner generiert. Jeder grüne Haken steht dabei für einen erfolgreich durchgeführten Step.

Fazit

Besonders leicht ist mir mit Gauge der Einstieg gefallen. Nach dem Herunterladen von Gauge installiert man mittels $ gauge --install java ein Sprachenplugin für Java und initialisiert durch den Befehl $ gauge --init java direkt sein Projekt. Das anschließend erstellte Gauge-Projekt hat eine einfache, verständliche Struktur. Die in Markdown geschriebenen Spezifikationen sind intuitiv und die Bezeichnungen der einzelnen Strukturelemente (Spezifikation, Szenario, Step) ist bei richtiger Anwendung selbsterklärend, so dass sie, wie bereits erwähnt, für Dokumentationszwecke oder zum Austausch mit kontextfernen Team-Mitgliedern genutzt werden können. Auf Grund der Step-Syntax können mit Gauge unkompliziert z.B. Integrations- oder E2E-Tests geschrieben werden. Die beiden momentan verfügbaren Report-Plugins HTML report und XML report generieren HTML respektive XML Reports für die Testausführung, so dass sich diese plattformübergreifend teilen oder veröffentlichen lassen.

Wie schon erwähnt unterstützt Gauge bereits einige Sprachen und IDEs. Zudem bietet ThoughtWorks eine Plugin-API an. Es ist also in Zukunft damit zu rechnen, dass weitere Sprachen und IDEs Gauge-Support bekommen.

Als abschließendes Fazit für mich kann ich nur jedem Entwickler, der sich durch den derzeitigen Sprachen- und IDE-Support angesprochen fühlt, empfehlen, Gauge für sich auszuprobieren. Der Einstieg ist einfach und unkompliziert und Gauge bietet eine Vielzahl hilfreicher Funktionen. Dazu zählen z.B. Context- und Teardown-Steps, das Versehen von Spezifikationen und Szenarien mit Tags, das Einlesen von Daten aus Text- und CSV-Datein und die Parameterübergabe aus Tabellen an Implementierungsfunktionen. Es ist insbesondere im Kontext von Behavior-Driven-Development, einer Technik bei der Aufgaben, Ziele und Ergebnisse einer Software textuell festgehalten und später als automatische Tests ausgeführt werden, ein Testframework über das man nachdenken könnte.

Codebeispiele

Alle gezeigten Codebeispiele lassen sich bei Github unter diesem Link einsehen und für eigenes Experimentieren klonen.

Quellen:
* Gauge Dokumentation
* Gauge Plugins
* Gauge Plugin API
* E2E-Test-Beispiel
* Behavior Driven Development
* Blogartikel zu Gauge

Diesen Beitrag teilen

Malte Kreutzfeldt
Software Development
Stets mit offenen Augen für neue Open Source Tools, Frameworks und Best Practice Entwicklungsmethoden lässt er der Erweiterung seines IT-Horizonts schier keine Grenzen.