Java-Projekt mit Playwright, TestNG und Allure (mithilfe des Page Object Model)
Ein neues Testautomatisierungsprojekt mit einem Tech-Stack aufzusetzen, mit dem man noch nicht vertraut ist, kann mitunter eine anspruchsvolle Aufgabe sein. In diesem Blog-Beitrag werde ich Schritt für Schritt erklären, wie du ein Grundgerüst für dein Testautomatisierungsprojekt mit Java, Playwright, TestNG und Allure aufbaust. Dieser Beitrag richtet sich insbesondere an weniger erfahrene Testingenieure, aber natürlich auch an alle anderen, die sich für das Thema interessieren.
Für alle die noch nicht mit Playwright vertraut sind – lest euch gerne unsere anderen Blog-Beiträge zum Thema durch.
Schritt 1: Vorbereitung
Bevor wir loslegen, müssen wir sicherstellen, dass unsere Entwicklungsumgebung gut gerüstet ist. Hier sind die Werkzeuge, die wir benötigen werden:
- Java Development Kit (JDK): Eine Version des JDK ist notwendig, um Java-Code schreiben zu können.
- Maven: Maven ist ein Build-Management-Tool, das uns hilft, unsere Abhängigkeiten zu verwalten und den Build-Prozess zu automatisieren.
- Eine IDE deiner Wahl: Egal ob IntelliJ IDEA, Eclipse oder eine andere IDE – wähle die, mit der du am besten arbeiten kannst.
Schritt 2: Projekt initialisieren
Wir fangen an mit einem frisch erstellten Maven-Projekt. In meinem Fall nutze ich dafür die IntelliJ Community Edition. Für die einfache Erstellung neuer Projekte bietet Maven sogenannten Archetypes, wir bleiben hier jedoch bei einem leeren Projekt.
Nach erfolgreicher Erstellung des Projekts, solltest du im Wurzelverzeichnis die Datei „pom.xml“ finden. Hier werden alle Abhängigkeiten und Konfigurationen verwaltet. Öffne Sie und füge dem Projekt die folgenden Abhängigkeiten hinzu:
<dependencies> <dependency> <groupId>com.microsoft.playwright</groupId> <artifactId>playwright</artifactId> <version>1.38.0</version> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>7.8.0</version> </dependency> <dependency> <groupId>io.qameta.allure</groupId> <artifactId>allure-testng</artifactId> <version>2.24.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.20.1</version> <scope>runtime</scope> </dependency> </dependencies>
Neben Playwright, dem Tool, dass wir zur Browserautomatisierung nutzen werden, nutzen wir außerdem TestNG, um unsere Tests zu organisieren und auszuführen. Allure und AspectJ Weaver nutzen wir für das Sammeln der Testergebnisse und die anschließende Erstellung aussagekräftiger Testberichte.
Schritt 3: BrowserFactory
Wir starten, indem wir unter src/test/java/org/example/factory eine Klasse mit dem Namen BrowserFactory anlegen. Diese ist ein Schlüsselstück unseres Projekts, da sie uns die Initialisierung von Browsern (um genau zu sein Chromium, Chrome, Firefox und Webkit) für unsere Tests ermöglicht. Weitere Informationen zur Browserkonfiguration gibt es hier.
package org.example.factory; public class BrowserFactory { private Playwright playwright; private Browser browser; private Properties properties; public Page initializeBrowser(String browserName, String headless) throws InvalidBrowserNameException { boolean isHeadless = Boolean.parseBoolean(headless); playwright = Playwright.create(); switch (browserName.toLowerCase()) { case "chromium": browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(isHeadless)); break; case "chrome": browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setChannel("chrome").setHeadless(isHeadless)); break; case "firefox": browser = playwright.firefox().launch(new BrowserType.LaunchOptions().setHeadless(isHeadless)); break; case "webkit": browser = playwright.webkit().launch(new BrowserType.LaunchOptions().setHeadless(isHeadless)); break; default: throw new IllegalArgumentException("Please provide a valid browser name (chrome, firefox, webkit or chromium)."); } BrowserContext browserContext = browser.newContext(); return browserContext.newPage(); } }
Optional können wir der BrowserFactory-Klasse eine Methode zur Initialisierung einer Konfigurations-Datei hinzufügen. Diese können wir beispielsweise mit Informationen versehen, die für die Browser-Initialisierung relevant sind. Für’s Erste hinterlegen wir in den config.properties jedoch nur eine Basis-URL.
src/test/resources/config/config.properties
BASE_URL=https://simplytest.de/
src/test/java/org/example/factory/BrowserFactory.java
public Properties initializeConfigProperties() { try { ClassLoader classLoader = getClass().getClassLoader(); String configUrl = classLoader.getResource("config/config.properties").getPath(); FileInputStream fileInputStream = new FileInputStream(configUrl); properties = new Properties(); properties.load(fileInputStream); } catch (IOException e) { e.printStackTrace(); } return properties; }
Schritt 4: Das erste Page Object
Nun legen wir unser erstes Page Object an. Dafür erstellen wir zuerst ein neues Package „pages“ und dann eine Klasse für unsere Homepage. In eurem eigenen Projekt muss das natürlich nicht die Homepage sein – diese soll hier nur als Beispiel dienen.
package org.example.pages; import com.microsoft.playwright.Locator; import com.microsoft.playwright.Page; public class HomePage { private final Page page; public HomePage(Page page) { this.page = page; } public Locator getHeading1() { return page.locator("h1"); } }
Die Beispiel-Methode „getHeading1“ dient dazu, später in unserem Test auf den Locator für unser <h1>-Element zugreifen zu können.
Für alle, die noch nicht mit dem Page Object Model (auch Page Object Pattern) vertraut sind: Das Ziel ist, in unseren Page Object-Klassen alle Interaktionen mit den jeweiligen Seiten kapseln und so alles rund um die Benutzeroberfläche von der Testlogik trennen. Somit erreichen wir u.a. eine bessere Wartbarkeit und Lesbarkeit unseres Test-Codes.
Schritt 5: Erstellung der Basis-Test-Klasse
Im nächsten Schritt legen wir eine neue Klasse BaseTest an, welche die Grundlage für unsere Tests bildet. Dazu erstellen wir zuerst ein Package „base“ und darin unsere Klasse. Wir fangen mit unserem Test-Setup an:
package org.example.base; public class BaseTest { private final BrowserFactory browserFactory = new BrowserFactory(); protected Properties properties = browserFactory.initializeConfigProperties(); protected Page page; protected HomePage homepage; private String browserName; @BeforeMethod @Parameters({"browserName", "headless"}) public void setUp(@Optional("chrome") String browserName, @Optional("false") String headless) throws IllegalArgumentException { this.browserName = browserName; page = browserFactory.initializeBrowser(browserName, headless); page.navigate(properties.getProperty("BASE_URL").trim()); homepage = new HomePage(page); } }
Die setUp-Methode wird vor jedem Test ausgeführt (erkennbar an der @BeforeMethod-Annotation, die uns TestNG
bietet). Die @Parameter-Annotation erlaubt uns, auf Testsuite-Ebene zu bestimmen, mit welchem Browser unsere Tests ausgeführt werden, und ob diese im Headless-Modus laufen. Um unsere Testsuite kümmern wir uns im späteren Verlauf des Artikels.
Um unabhängig von unserer Testsuite eine Konfiguration für die Einzelausführung von Tests zu haben, nutzen wir hier die Annotation @Optional. Somit können wir unsere Tests während der Entwicklung z.B. im Headed Chrome-Browser, später aber z.B. in der Jenkins-Pipeline im Headless Firefox ausführen.
Dann initialisieren wir hier den Browser, rufen unsere Basis-URL auf, die wir aus unserer Test-Konfiguration ziehen und erstellen eine Instanz unserer Homepage, die dann unseren zukünftigen Test-Klassen zur Verfügung steht.
Nun kümmern wir uns um das, was nach dem Test passiert:
@AfterMethod public void tearDown(ITestResult result) throws IOException { if (result.getStatus() == ITestResult.FAILURE) { LocalDateTime currentDateTime = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"); String formattedDateTime = formatter.format(currentDateTime); String resultName = result.getName() + "_" + browserName + "_" + formattedDateTime; Path screenshotPath = Paths.get("./target/screenshots/" + resultName + ".png"); page.screenshot(new Page.ScreenshotOptions().setPath(screenshotPath)); } page.context().browser().close(); }
Wir fügen nun die Methode „tearDown“ hinzu und versehen Sie mit der TestNG’s @AfterMethod. Hier holen wir uns das Testergebnis ein und erstellen im Falle eines fehlgeschlagenen Tests einen Screenshot. Um die Screenshots von einander unterscheiden zu können, benennen wir den Screenshot nach der Testmethode, dem Testbrowser sowie dem aktuellen Datum und der Uhrzeit.
Anschließend erledigen wir noch die nötigen Aufräumarbeiten, indem wir den Testbrowser schließen.
Zu guter Letzt ergänzen wir die BaseTest-Klasse noch um eine Methode „attachScreenshotToAllureReport“, damit unsere Screenshots als Anhang in unserem Allure Testbericht sichtbar werden. Damit dies erwartungsgemäß funktioniert, müssen wir diese nur noch nach Erstellung des Screenshots aufrufen. Zum Schluss schaut unsere BaseTest-Klasse folgendermaßen aus:
public class BaseTest { private final BrowserFactory browserFactory = new BrowserFactory(); protected Properties properties = browserFactory.initializeConfigProperties(); protected Page page; protected HomePage homepage; private String browserName; @BeforeMethod @Parameters({"browserName", "headless"}) public void setUp(@Optional("chrome") String browserName, @Optional("false") String headless) throws IllegalArgumentException { this.browserName = browserName; page = browserFactory.initializeBrowser(browserName, headless); page.navigate(properties.getProperty("BASE_URL").trim()); homepage = new HomePage(page); } @AfterMethod public void tearDown(ITestResult result) throws IOException { if (result.getStatus() == ITestResult.FAILURE) { LocalDateTime currentDateTime = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"); String formattedDateTime = formatter.format(currentDateTime); String resultName = result.getName() + "_" + browserName + "_" + formattedDateTime; Path screenshotPath = Paths.get("./target/screenshots/" + resultName + ".png"); page.screenshot(new Page.ScreenshotOptions().setPath(screenshotPath)); attachScreenshotToAllureReport(resultName, screenshotPath); } page.context().browser().close(); } @Attachment(type = "image/png") private void attachScreenshotToAllureReport(String resultName, Path screenshotPath) throws IOException { Allure.addAttachment(resultName, new ByteArrayInputStream(Files.readAllBytes(screenshotPath))); } }
Schritt 6: Der erste Test
Nachdem nun alle Vorbereitungen getroffen wurden, wird es nun Zeit für den ersten Test. Dazu erstellen wir erneut ein neues Package, diesmal mit dem Namen „tests“ und dann eine Java-Klasse. Wichtig ist, dass diese eine Sub-Klasse unserer BaseTest-Klasse ist.
Unsere erste Test-Methode versehen wir mit der TestNG-Annotation @Test. Dadurch lässt sich besagte Methode samt setUp und tearDown, die wir im vorherigen Schritt programmiert haben, nun gesondert ausführen und später in unsere Testsuite integrieren.
Im Zuge des ersten Tests holen wir uns den Locator für das <h1>-Element unserer Homepage über die zuvor erstellte Page Object Klasse ein und prüfen mithilfe von Playwright, ob diese sichtbar ist. Etwa so:
package org.example.tests; import com.microsoft.playwright.assertions.PlaywrightAssertions; import org.example.base.BaseTest; import org.testng.annotations.Test; public class HomepageTest extends BaseTest { @Test public void verifyHeadingIsVisible() { PlaywrightAssertions.assertThat(homepage.getHeading1()).isVisible(); } }
Schritt 7: Testsuite
Üblicherweise wollen wir mehrere Tests gebündelt ausführen. Im Falle von TestNG funktioniert das mithilfe einer einfachen XML-Datei.
Unter dem Pfad src/test/resources/testrunners legen wir nun unsere erste Testsuite an. In diesem Fall nenne ich sie „regression.xml“. Darin können wir nun bestimmen, welche Tests unter der Berücksichtigung bestimmter Parameter ausgeführt werden sollen. Im Falle meines Beispiels sollen meine Regressionstests im Headless Chrome Browser ausgeführt werden.
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" > <suite name="Regression Suite Chrome" verbose="1"> <parameter name="headless" value="true"></parameter> <parameter name="browserName" value="chrome"></parameter> <test name="Homepage Tests"> <classes> <class name="org.example.tests.HomepageTest"/> </classes> </test> </suite>
Weitere Informationen zur Konfiguration der Testsuite sowie zur Ausführung über die Kommandozeile gibt es hier.
Schritt 8: Testbericht öffnen
Super, wir sind fast am Ziel! Nachdem die Tests erfolgreich durchgeführt wurden, ist es an der Zeit, den Testbericht zu öffnen und die Ergebnisse anzusehen. Dafür navigieren wir in das Wurzelverzeichnis unseres Projekts und suche nach dem Ordner „allure-results“. Hier befindet unser Bericht im JSON-Format.
Um nun den Bericht anzeigen zu lassen, öffnen wir die Kommandozeile oder das Terminal und navigieren zum Projektordner. Dort geben wir folgenden Befehl ein:
allure serve allure-results
Dadurch wird ein lokaler Server gestartet, der es uns ermöglicht, den Bericht in einem Webbrowser anzeigen zu lassen. Der Standardbrowser sollte sich nun automatisch öffnen und den Testbericht anzeigen.
Besonders interessant sind die Allure Testberichte in Verbindung mit CI/CD Tools wie Jenkins, Bamboo oder TeamCity. Genaueres dazu gibt es in der Allure Dokumentation zu lesen.
Fazit
Herzlichen Glückwunsch! Du hast jetzt ein Java-Projekt aufgesetzt, mit dem du Playwright, TestNG und Allure nutzen kannst, um Webanwendungen zu testen. Mit dem Page Object Model behältst du stets den Überblick über deine Seitenstruktur.
By the way, für einen Schnellstart kannst du unser Github Template nutzen.
Ich hoffe, dieser Blog-Beitrag konnte dir bei deinem Projekt-Start helfen oder dich einfach nur dazu inspirieren, diesen Stack bei deinem nächsten Projekt auszuprobieren.
Happy Testing! 🚀
Redakteur auf testautomatisierung.org
Testautomatisierungsingenieur bei SimplyTest GmbH, Nürnberg
www.simplytest.de
Hinterlasse einen Kommentar
An der Diskussion beteiligen?Hinterlasse uns deinen Kommentar!