Testautomatisierung mit Playwright

Einbindung von Playwright unter JUnit

Nachdem wir in einem anderen Artikel die grundlegende Einrichtung und Verwendung von Playwright demonstriert haben, wollen wir nun Playwright einsetzen, um mit JUnit die echten Tests einer Webanwendung zu schreiben. Hierfür müssen wir entsprechend die pom.xml-Datei unseres Maven-Projekts anpassen. In diesem Tutorial verwenden wir JUnit Jupiter aka JUnit 5. Genauso wie für die Playwright API, legen wir eine neue JUnit <dependency> unter dem XML-Knoten <dependencies> an.

  <dependencies>
    <dependency>
      ...
    </dependency>

    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>5.7.2</version>
      <scope>test</scope>
    </dependency>
    
  </dependencies>

Möglicherweise ist bereits eine junit-Abhängigkeit beim Anlegen des Projekts erstellt worden, wir können sie aber einfach entfernen. Normalerweise benötigt JUnit Jupiter 2 Abhängigkeiten. Die junit-jupiter-api für Annotationen wie @Test, um Testmethoden zu annotieren, und die junit-jupiter-engine, damit die Tests ausgeführt werden. Intern ist jedoch die junit-jupiter-engine abhängig von der Jupiter API, weshalb es ausreicht, nur die TestEngine als Abhängigkeit einzufügen. Der scope-Tag gibt an, für welche Phasen des Builds sich die jeweiligen Abhängigkeiten eignen – in unserem Fall also für die Testkompilierung und -ausführung.

Testklasse schreiben

Wir wollen nun im Rahmen unseres ersten Testfalls überprüfen, ob der Titel der Wikipedia-Seite tatsächlich auch Wikipedia lautet. Dazu legen wir eine neue Testklasse WikipediaTest.java an, die unseren Testfall, verpackt in einer Unit-Test Methode, beinhalten wird. Im package src>test>java>de.simplytest können später beliebig viele weitere Testklassen hinzukommen.

1. Testfall: „Überprüfung des Webseitentitels“

package de.simplytest;

import com.microsoft.playwright.*;
import org.junit.jupiter.api.*;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class WikipediaTest {

    @Test
    public void checkWikipediaTitle() {
        Playwright playwright = Playwright.create();
        Browser browser = playwright.firefox().launch();
        Page page = browser.newPage();

        page.navigate("https://www.wikipedia.org/");

        String actualTitle = page.title();
        assertEquals("Wikipedia", actualTitle);

    }

}

Die Playwright-, Browser- und Page- Objektinstanziierung sollten bereits bekannt sein. Den Titel der Seite bekommen wir mit dem API-Aufruf page.title() und speichern diesen im String actualTitle. Der wirkliche Test erfolgt jetzt mit dem Aufruf assertEquals, der von JUnit bereitgestellt wird. Dieser Methode übergeben wir als Argumente den Titel der Webseite, den wir erwarten (also „Wikipedia“) und den eigentlichen Titel, den uns Playwright von der Webseite liefert. Intern prüft assertEquals dann die beiden Argumente auf Gleichheit. Assert-Methoden sind allgemein nützlich zur Bestimmung des Pass- oder Fail-Status eines Testfalls. Es gibt natürlich neben assertEquals auch weitere Asserts, z.B. assertTrue, um zu überprüfen, ob eine Bedingung eintritt, oder assertSame, um zu prüfen, ob 2 Objekte dasselbe Objekt referenzieren. Eine komplette Übersicht aller Asserts ist auf der Doc-Seite zu finden.

2. Testfall: „Überprüfung eines Wikipedia-Artikels“

Um sicherzustellen, dass eine Webseite funktioniert, reicht es nicht aus, nur zu prüfen, ob die Seite geladen und richtig angezeigt wurde, indem man zum Beispiel nur den Titel der Webseite prüft. Vielmehr muss sichergestellt werden, dass die Inhalte auf der Webseite korrekt sind und die Interaktionen ordnungsgemäß funktionieren. In einem zweiten Testfall testen wir daher, ob die Websuche auf der Seite beim Eintippen des Suchbegriffs Koala funktioniert und zum richtigen Ergebnis führt.

Ausschnitt der Wikipedia-Startseite

Für das Eintippen eines Suchbegriffs müssen wir jedoch zuerst das richtige Suchfeld auf der Webseite finden. Dazu sucht Palywright die Elemente einer Webseite nicht anhand ihrer visuellen gerenderten Repränsentation im Webbrowser, sondern anhand ihrer technischen Entsprechung  im sogenannten DOM-Baum mittels XPATH und CSS Selektoren. Diese Selektoren sind nichts anderes als Strings, die auf Elemente auf der Seite zeigen, und werden dafür verwendet, um Aktionen an diesen Elementen durchzuführen, wie wir weiter unten im Codebeispiel sehen werden.

Um einen geeigneten Selektor für das Suchfeld herauszufinden, klicken wir mit der rechten Maustaste über dieses Feld und wählen den Kontextmenüpunkt Element untersuchen aus. Aus dem DOM-Baum erkennen wir, dass das Suchfeld anhand der ID searchInput eindeutig identifiziert werden kann, die wir in unserem Java-Test verwenden.

Visualisierung eines HTML-Elements mit dem Inspektor

Nachdem das Suchfeld anhand der ID identifiziert wurde, können wir damit interagieren und den gewünschten Suchbegriff von der Playwright API eintippen lassen.

@Test
    public void checkWikipediaArticle() {
        Playwright playwright = Playwright.create();
        Browser browser = playwright.firefox().launch();
        Page page = browser.newPage();

        page.navigate("https://www.wikipedia.org/");
        page.fill("#searchInput", "Koala");
        page.keyboard().press("Enter");

        String actualArticleHeading = page.textContent("#firstHeading");

        assertEquals("Koala", actualArticleHeading);
    }

Wir verwenden dazu die Befehle Page.fill(), der auf das Element mit dem Selektor "#searchInput" wartet, das in diesem Beispiel das Suchfeld darstellt, und füllt dieses mit dem Suchbegriff "Koala" aus. Anschließend simulieren wir das Drücken der Eingabetaste mit dem Page.keyboard().press("Enter")-Befehl.

Dadurch haben wir die Suchanfrage automatisiert! Uns bleibt jetzt noch zu prüfen, ob die Suche funktioniert hat und der richtige Artikel gefunden wurde. Hierfür lassen wir uns von Playwright mit dem Befehl Page.textContent() den Inhalt des Elements mit dem Selektor "#firstHeading" zurückgeben und überprüfen ihn, wie im vorherigen Beispiel, mit einem assertEquals auf Gleichheit mit dem Wort Koala.

Der Selektor #firstHeading zeigt auf das rot unterstrichene Element. Der Textinhalt lautet in diesem Bild „Koala“

Code strukturieren

Wir initialisieren eine Playwright– und Browser-Instanz global, damit sie über alle Tests hinweg verwendet werden kann. Außerdem lassen wir JUnit für uns vor jeder Testmethode ein Page-Objekt erstellen, damit wir nicht in jedem Test die jeweiligen Instanzen und Objekte einzeln erstellen müssen. Für einen isolierten Browserzustand zwischen den Tests, erstellen wir auch vor jedem Test einen neuen BrowserContext. Die Annotationen @BeforeAll, @AfterAll, @BeforeEach und @AfterEach sind dabei sehr hilfreich.

Somit können wir die Tests unabhängig voneinander in einem frischen Zustand durchführen, ohne dass sich die Tests beispielsweise durch Cookies gegenseitig beeinflussen, und am Ende die Browserinstanzen ebenfalls unabhängig vom Teststatus freigeben. Wenn wir jedoch die Cookies-Funktionalität einer Webseite testen wollen, zum Beispiel für eine Authentifikation, dürfen wir auf keinen Fall neuen Instanzen von BrowserContext vor jeder Testmethode erstellen. Die Cookies müssen auf jeden Fall über die Tests hinweg erhalten bleiben!

package de.simplytest;

import com.microsoft.playwright.*;
import org.junit.jupiter.api.*;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class WikipediaTest
{
    //Shared between all tests in this class
    private static Playwright playwright;
    private static Browser browser;

    // New instance for each test method
    private Page page;
    private BrowserContext browserContext;

    @BeforeAll
    public static void setUpClass() {
        playwright = Playwright.create();
        browser = playwright.firefox().launch();
    }

    @AfterAll
    public static void tearDownClass() {
        browser.close();
        playwright.close();
    }

    @BeforeEach
    public void createContextAndPage() {
        browserContext = browser.newContext();
        page = browserContext.newPage();
    }

    @AfterEach
    public void destroyContext() {
        browserContext.close();
    }

    @Test
    public void checkWikipediaTitle() {
        page.navigate("https://www.wikipedia.org/");
        String actualTitle = page.title();
        assertEquals("Wikipedia", actualTitle);
    }

    @Test
    public void checkWikipediaArticle() {
        page.navigate("https://www.wikipedia.org/");
        page.fill("#searchInput", "Koala");
        page.keyboard().press("Enter");

        String actualArticleHeading = page.textContent("#firstHeading");

        assertEquals("Koala", actualArticleHeading);
    }
}

Will man nun die Tests ausführen, gibt man im Terminal den Befehl mvn test ein. Dieser führt alle Testklassen aus (in unserem Beispiel haben wir nur eine Testklasse, die WikipediaTest.java-Klasse. Um einzelne Testklassen zu starten, verwendet man mvn test -Dtest=Testklasse. Möglich ist außerdem das Starten einzelner Methoden innerhalb einer Testklasse, z.B. mit mvn test -Dtest=WikipediaTest#checkWikipediaArticle.

Maven Surefire Plugin

Für die Ausführung der Unit-Tests einer Anwendung während der Testphase ist das Surefire Plugin verantwortlich. Das Surefire Plugin sucht nach Testklassen, deren Namen den folgenden Mustern entsprechen:

  • **/Test*.java
  • **/*Test.java
  • **/*Tests.java
  • **/*TestCase.java

Da unsere Testklasse WikipediaTest mit Test endet, mussten wir uns nicht darum kümmern, dass Surefire die Klasse findet. Würden wir jedoch andere Namen für unsere Testklassen verwenden, müssten wir dies in der pom.xml-Datei für das Plugin berücksichtigen. Beispielsweise beachtet Surefire bei der Suche auch auf Testklassen, die mit dem Namen Simply anfangen.

        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>3.0.0-M5</version>
          <configuration>
            <includes>
              <include>**/Simply*.java</include>
            </includes>
          </configuration>
        </plugin>

E2E-Tests haben u.a. den Nachteil, instabil und langsam zu sein. Aus diesem Grund hat Microsoft die Open-Source Playwright-API entwickelt, die als E2E Testframework der nächsten Generation gilt. Mit Playwright verfolgt man das Ziel, die browserübergreifende Webautomatisierung schneller und zuverlässiger zu gestalten. Dabei basiert Playwright auf einer ereignisgesteuerten (sog. event-driven) Architektur, welche automatisch auf Browser-Ereignisse wie Seitennavigation, Netzwerkanfragen oder DOM-Änderungen wartet. Mit neueren Versionen kommen immer weitere Features hinzu wie beispielsweise Geolokalisierung oder Mobile Emulation, um mobile Webseiten zu testen. Playwright API wird für verschiedene Programmiersprachen bereitgestellt: Java, Python, C#, JavaScript / TypeScript. In diesem Beitrag geht es um die Einrichtung von Playwright mit Java.

1. Einrichtung der Entwicklungsumgebung

Wir benötigen zuerst eine Entwicklungsumgebung für Java, die auf der folgenden Seite heruntergeladen werden kann: https://www.jetbrains.com/de-de/idea/download/. Einfach die Community-Version auswählen, die .exe-Datei ausführen und die Installationsschritte durchführen.

2. Maven installieren

Zur schnellen und einfachen Einrichtung von Playwright verwenden wir das Build-Management-Tool Maven, mit welchem die Playwright API Bibliotheken als Abhängigkeiten in Java Projekte automatisiert eingebunden werden. Zur Installation von Maven gehen wir wie folgt vor:

  1. https://maven.apache.org/download.cgi besuchen und das Binary zip-Archiv herunterladen
  2. Die Datei in einem beliebigen Ordner entpacken, z.B. in C:/ program files /
  3. Nach dem Entpacken, den bin-Ordner zum Systempfad hinzufügen

Systemumgebungsvariablen bearbeiten

In der Suchleiste env eintippen und Systemumgebungsvariablen bearbeiten auswählen

Umgebungsvariablen auswählen

Auf Umgebungsvariablen klicken

Path bearbeiten

Path auswählen und auf Bearbeiten klicken

bin-Ordner auswählen

Neuen Eintrag erstellen, zum bin-Ordner von dem entpackten Apache-Maven Archiv navigieren und diesen selektieren

  1. cmd.exe öffnen und mit mvn -v oder mvn –version überprüfen, ob Maven richtig installiert wurde

Bei Problemen die Hinweise zur Installation beachten: https://maven.apache.org/install.html

3. Neues Playwright Projekt erstellen

Mit Maven sollen möglichst viele Schritte automatisiert werden, wie etwa die Erstellung eines Projekts, das Testen usw. Die sog. Archetypes bieten eine Art „Gerüst“ für unterschiedliche Typen von Softwareprojekten an, deren Struktur dem Standard von Maven entspricht. Der Befehl mvn -B archetype:generate -DgroupId=de.simplytest -DartifactId=mein-maven-projekt -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 erstellt das einfachste Maven-Projekt in einem Verzeichnis mit dem Namen mein-maven-projekt . Mehr über die Maven-Archetypes erfährt man hier. Den Ordner öffnen wir in Intellij und fügen Playwright unter dem XML-Knoten dependencies in der pom.xml-Datei ein. Falls eine Warnung kommt, einfach auf Trust Project klicken.

  <dependencies>
    <dependency>
      ...
    </dependency>

    <dependency>
      <groupId>com.microsoft.playwright</groupId>
      <artifactId>playwright</artifactId>
      <version>1.14.1</version>
    </dependency>
    
  </dependencies>

Außerdem sollten wir unter dem properties-Tag maven compiler source und maven compiler target zu 1.8 ändern, um die aktuellen Java 8 Features verwenden zu können.

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

4. Erste Schritte mit Playwright in Java – „Titel einer Seite ausgeben“

In mein-maven-projekt > src > main > java > de.simplytest > App.java befindet sich die main-Methode, die automatisch mit der Generierung der Archetype angelegt wird. Wir können natürlich aber ebenfalls weitere Java-Klassen erstellen und zum Projekt hinzufügen. Nehmen wir den folgenden Code als Programmbeispiel. Hier starten wir Firefox, navigieren dann zur offiziellen Playwright-Webseite und geben anschließend den Titel der Seite aus.

package de.simplytest;

import com.microsoft.playwright.*;

public class App 
{
    public static void main(String[] args )
    {
        try (Playwright playwright = Playwright.create()) {
            Browser browser = playwright.firefox().launch(new BrowserType.LaunchOptions().setHeadless(false));
            Page page = browser.newPage();
            page.navigate("http://playwright.dev");
            System.out.println(page.title());
        }
    }
}

Playwright öffnet den Browser standardmäßig ohne User Interface im sogenannten headless-Modus, also sehen wir nicht wirklich die interaktive Ausführung des Programms. Um das zu ändern, setzen wir mittels LaunchOptions den headless-Flag auf false und eventuell auch setSlowMo, um die Ausführungszeit der API zu verlangsamen.

Programm starten

Als Letztes geben wir folgenden Befehl ein, um das Programm über Terminal auszuführen:

mvn compile exec:java -Dexec.mainClass=de.simplytest.App

Ergebnis in der Konsole nach der Ausführung des Programms
Am Ende sehen wir den Titel der Webseite : Fast and reliable end-to-end testing for modern web apps