In diesem Tutorial erstellen wir gemeinschaftlich ein kleines Beispielprojekt mit den folgenden Technologien und Methoden:
- Node.js
- npm
- Visual Studio Code
- Playwright
- Gherkin
- Cucumber
- Page Object Model
Als Erstes werden die einzelnen Technologien erläutert, diese danach installiert und anschließend das Projekt aufgesetzt.
Das Tutorial ist primär für Anfänger konzipiert, wobei jeder Teil bei bereits vorhandenem Wissen,
einfach übersprungen, und zum nächsten Teil übergegangen werden kann.
Erläuterung verwendeter Technologien
Node.js
Node.js ist eine plattformunabhängige Open-Source Laufzeitumgebung für die Sprache Javascript (äquivalent zu Java Virtual Maschine für Java), um diese außerhalb des Webbrowsers nutzen zu können. Für unser Projekt wird diese benötigt, da Cypress auf Node.js und Javascript basiert.
npm
Wie der Name Node Package Manager bereits verrät, handelt es sich hierbei um einen freien Paketmanager, basierend auf der Javascript-Laufzeitumgebung Node.js. Mit diesem kann man einfach und bequem die für das eigene Projekt notwendigen Module (im folgenden als Synonym für Pakete genutzt) installieren und verwalten. Des Weiteren fungiert npm auch als CLI (Kommandozeilenprogramm), um Module ausführen zu können.
Mit dem Befehl:
npm install [Parameter] <packagename>
können benötigte Module installiert werden.
- Parameter: Werte die die Installation verändern, zum Beispiel -g für eine globale Installation.
- packagename: Der Name des zu installierenden Moduls.
Ein wichtiger Aspekt für die Entwicklung ist, das Module jeweils global oder lokal installiert werden können.
Lokal
npm install Paketname
Ein Modul wurde nur für das Verzeichnis installiert, in welchem der Paketmanager aufgerufen wurde (Wurzelverzeichnis des Projektes) und kann daher nur in diesem genutzt werden. Man nutzt dies, wenn man spezielle Module nur für ein Projekt benötigt, oder wenn man verschiedene Versionen desselben Moduls für unterschiedliche Projekte nutzt. Die Module werden dann in den Ordner node_modules des Projektes installiert.
Global
npm install -g Paketname
Ein Modul wird Global auf dem Rechner zur Nutzung für alle Projekte installiert. Wo diese Module installiert worden hängt vom System ab.
Welche Module und wo diese installiert sind, können wir uns im Terminal anzeigen lassen, dabei kann zwischen lokalen und globalen Modulen unterschieden werden:
npm list
zeigt alle für das Verzeichnis / Projekt lokal installierten Module an, und muss auch im Projektordner ausgeführt werden.
npm list -g
zeigt alle global installierten Module an, und kann überall ausgeführt werden.
Playwright
Playwright ist ein Frontend-Testautomatisierungstool für Webanendungen, programmierbar in Typescript.
Näheres über Cypress erfahren wir hier: https://www.testautomatisierung.org/cypress/
Gherkin
Gherkin ist eine Beschreibungssprache zur effektiven Gestaltung von Anforderungen im Behavior-Driven-Development Stil.
Näheres über Cypress erfahren wir hier: https://www.testautomatisierung.org/lexikon/gherkin/
Cucumber
Plattformunabhängiges Framework zur Abbildung von in Gherkin geschriebenen Anforderungen auf automatisierte Tests in den im Projekt genutzten Programmiersprachen wie beispielsweise Java, Ruby, C++ oder Javascript
Näheres über Cypress erfahren wir hier: https://www.testautomatisierung.org/lexikon/cucumber/
Page Object Model (POM)
Das Page Object Model (POM) ist ein bewährtes Entwurfsmuster in der Testautomatisierung, das darauf abzielt, gemeinsame Interaktionen mit einer Webanwendung, wie etwa einen Login-Vorgang, zu abstrahieren.
Hauptziel des POM ist die Steigerung der Wartbarkeit, Lesbarkeit und Erweiterbarkeit von Testskripten. Durch die klare Strukturierung von Seitenobjekten werden wiederkehrende Aktionen und Elemente effizient organisiert und können leicht in verschiedenen Testszenarien wiederverwendet werden.
Installation verwendeter Technologien
Node.Js Installieren unter Windows
Wir gehen auf die Seite von Node.js (https://nodejs.org/en/download) und downloaden die Version unter Recommended For Most Users.
Folgt dem Installationsmenü, beachtet dabei, dass im Schritt Custom Setup der Punkt Add to PATH ausgewählt ist.
Das ist wichtig, da dies node.js unter Windows in einer fest durch einen Standard definierten globalen Systemvariable speichert, und somit jedes Programm welches auf Node.js zugreifen möchte, dies findet.
Nach der Installation führen wir eine Command Promt (Windows Kommandozeile) aus, indem wir cmd in das Windows-Suchfeld eingeben und das Programm Command Prompt
aus den Vorschlägen anklicken.
In der Commandozeile überprüfen wir ob Node.js korrekt installiert mit dem Befehl:
node -v
wobei -v für Version steht.
Die angezeigt Version sollte der entsprechen, welche wir installiert haben.
Visual Studio Code installieren unter Windows
Wir gehen auf die Seite von Visual Studio Code: https://code.visualstudio.com/, downloaden die Version unter Download for Windows – Stable Build, und folgen dem Installationsmenü.
Projekt aufsetzen
Virtual Studio Code vorbereiten
Projektordner erstellen
Wir erstellen einen Ordner mit dem Namen unserer Wahl und öffnen anschließend Visual Studio Code.
Den Ordner in VS Code öffnen wir über File -> Open Folder und wählen den von uns benannten Ordner aus.
Erweiterung(Plugin) installieren
Um effektiv mit Gherkin und Cucumber unter VS Code arbeiten zu können, installieren wir in VS Code das Plugin: Cucumber (Gherkin) Full Support. Es ermöglicht, die in Gherkin definierte Beschreibungssprache in den von uns erstellten Feature-Dateien farblich hervorzuheben.
Dazu gehen wir in VS Code links auf das Symbol Extensions, geben in die Suchleiste Cucumber ein, und installieren das im Bild gezeigte Plugin.
Projekt initialisieren
Um ein Projekt mit node.js zu initialisieren, nutzen wir den Paketmanager npm. Wir nutzen den Befehl npm init -y, wobei das -y ein zusätzlicher Parameter ist, wodurch npm das Projekt mit Defaultwerten aufsetzt, wie im folgenden Bild in der Ausgabe im Terminal zu sehen ist.
Was hier ausgegeben wird, ist die von npm inital erstellte package.json.
Diese ist das Herzstück des Projektes und definiert die benötigten Abhängigkeiten.
Sie ist das Äquivalent zur pom-Datei aus dem Java-Maven Umfeld.
- name: Offizieller Name des Projektes
- version: Aktuelle Version des Projektes
- description: Beschreibung des Projektes
- main: Der Einstiegspunkt des Hauptprogramms des Projektes
- scripts: Hier können Scripts zur einfacheren Ausführung mit NPM eingefügt werden
- keywords: Wörter die das Modul beschreiben, mit denen das Modul bei Veröffentlichung durch Suchbegriffe gefunden werden kann
- author: Der Entwickler des Projektes
- license: Die Lizenz unter der die Software betrieben werden soll, default Open-Source-Lizenz des ISC (Internet System Consortium)
Die package.json finden wir danach in unserem Projektordner und diese sollte der Ausgabe im Terminal entsprechen:
{
"name": "cypress_cucumber_gherkin_pageobjects",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Module installieren
Im folgenden installieren wir zunächst alle für das Projekt erforderlichen Module
Playwright
Wir installieren Playwright mit npm über den Befehl:
npm install @playwright/test --save-dev
als devDependency.
Ts-node
Des Weiteren installieren wir ts-node mit npm über den Befehl:
npm install ts-node --save-dev
als devDependency.
Dieses Modul benötigen wir, um vor der Ausführung unserer Tests, Typescript in Javascript umzuwandeln.
Cucumber
Cucumber für Playwright installieren wir mit npm im Terminal über den Befehl:
npm install @cucumber/cucumber --save-dev
als devDependency.
Um unsere Feature Files mit unseren Step Files von Cucumber miteinander zu verbinden, und anschließend die Feature Files als Testsuites nutzen zu können, müssen wir eine cucumber.js Datei anlegen, die als Runner für die Tests fungiert. (Diese ist ähnlich zum Testrunner von Cucumber im Java Umfeld)
Dafür erstellen wir eine neue Datei: cucumber.js im Wurzelverzeichnis des Projektes, welche wie folgt aussieht:
let options = [
'--require-module ts-node/register', (1)
'--require ./steps/*.steps.ts', (2)
'--format progress', (3)
].join(' ');
let run_features = [
'./features/', (4)
options,
].join(' '); (5)
module.exports = {
test_runner: run_features (6)
};
(1) Es wird das Modul „ts-node“ in die Ausführung von Cucumber integriert
(2) Es werden alle Steps aus dem Ordner „steps“ im Wurzelverzeichnis nach dem Schema *.steps.ts in die Ausführung von Cucumber integriert
(3) Es wird definiert wie der Fortschritt der Testausführung in der Konsole dargestellt werden soll (Hier als Punkte)
(4) Es werden die Feature Files in die Ausführung von Cucumber integriert
(5) Die Strings werden mit der Methode join mit dem Leerzeichen als Trennzeichen zu einem String zusammengefasst
(6) Es wird der gesamte Scriptstring unter dem Namen test_runner exportiert
Die Cucumber Tests starten wir über den Befehl:
npx cucumber-js -p test_runner
Wir starten hier das Modul cucumber und hängen die vorher von uns in der cucumber.js definierten Werte als Parameter über die Variable test_runner mit an.
Testscript in der Package.json
Damit wir diesen Befehl nicht jedes Mal eingeben müssen, erstellen wir uns ein einfaches Script.
Im npm Umfeld ist es möglich Scripts für die Ausführung zu erstellen, indem wir diese in der package.json definieren.
Da diese eine Json Datei ist, können unter dem Schlüssel scripts : { } alle benötigten Scripts als Schlüssel : Wert Paar angelegt werden.
Diese Scripts können dann über den Befehl:
npm [Scriptname]
im Terminal ausgeführt werden.
Wir legen uns das folgende Script an:
"test": "npx cucumber-js -p test_runner"
Unsere package.json sollte dann wie folgt aussehen:
{
"name": "playwright_cucumber_gherkin_pageobjects",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "npx cucumber-js -p test_runner"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@cucumber/cucumber": "^10.0.1",
"@playwright/test": "^1.39.0",
"ts-node": "^10.9.1"
}
}
wichtig ist zu beachten, dass die Versionsnummern sich unterscheiden können.
Anschließend können wir unsere Cucumbertests mit dem Befehl:
npm test
ausführen, und diese wenn gewollt, so auch in eine CI/CD Pipeline integrieren.
Durchführung des Projektes
Die Durchführung dieses Beispielprojektes erfolgt nach den Prinzipien moderner Qualitätssicherung:
- Anforderungsanalyse nach BDD und Ableitung entsprechender Features mit Gherkin
- Technische Analyse der geplanten Umsetzung, aus welcher wir die PageObjects für die Testautomatisierung ableiten und erstellen
- Erstellen der Steps aus den Feature Files mit der Testautomatisierungstechnologie (hier Playwright), in der entsprechenden Sprache (hier TypeScript)
unter Verwendung unserer gut wart- und wiederverwendbaren PageObjects.
Als Beispielwebanwendung nutzen wir den Login der Simplytest eigenen Bankingdemoanwendung unter : http://testorbit.germanywestcentral.cloudapp.azure.com:4200.
Wir gehen für die Übung davon aus, dass wir mit dem gesamten Team in der Three Amigos Session (Näheres finden wir hier: 3 Amigos) die folgenden Scenarios für das Feature: UserLogin ausgearbeitet haben.
Feature File
Dafür erstellen wir uns die Datei UserLogin.feature im Ordner cypress/e2e/feature.
In diese schreiben wir das folgende Scenario:
Feature: UserLogin (1)
Scenario Outline: Ein Nutzer kann sich mit korrekten Nutzerdaten einloggen (2)
Given Der Nutzer ist auf der Startseite (3)
When Der Nutzer gibt korrekte Daten für '<Username>' und '<Password>' ein
Then Der Nutzer wird korrekt eingeloggt
Examples: (4)
| Username | Password |
| 00001 | demo | (5)
- (1) Der Name des zu testenden Features
- (2) Die Beschreibung des Szenarios. Outline ist ein Befehl, um die Daten aus der Examples: Liste dem Step übergeben zu können.
- (3) Die umzusetzenden Steps in der Feature File
- (4) Examples: Ein Tabelle bei der die Werte unter den Headervariablen direkt von Cucumber auf die entsprechenden Variablen in den Steps abgebildet werden. Hier werden Username auf ‚<Username>‘ und Password auf ‚<Password>‘ abgebildet. Cucumber führt die Szenarien dann für jeden Datensatz einmal aus.
- (5) Beispieldatensatz: Username = 00001 , Password=demo.
PageObject mit Playwright in TypeScript
Nun analysieren wir die Seite der Webanwendung, auf der sich der Login befindet, überlegen uns welche der Elemente wir für das PageObject LoginPage benötigen.
Dabei ist zu beachten, dass wir in das PageObject LoginPage nur die Elemente aufnehmen, welche nur auf dieser Seite vorhanden sind.
Wenn wir nun die Seite betrachten:
sehen wir, dass wir für unsere Steps aus dem Feature UserLogin die Elemente:
- Inputfeld: ContractID (ContractID eingeben)
- Inputfeld: Password (Password eingeben)
- Button: Login (Login Button drücken)
benötigen werden.
Wir schauen uns mit den Entwicklertools die Seite an und sehen, dass alle Felder jeweils ein Attribut id und data-testid besitzen.
Mit Playwright können wir die Elemente jeweils wie folgt ansprechen:
id
- Inputfeld: ContractID => page.locator(‚[id=“contract_input“]‘)
- Inputfeld: Password => page.locator(‚[id=“password_input“]‘)
- Button: Login => page.locator(‚[id=“login-button“]‘)
data-testid
- Inputfeld : ContractID => page.locator(„input[data-testid=’contract_input‘]“)
- Inputfeld : Password => page.locator(„input[data-testid=’password_input‘]“)
- Button : Login => page.locator(„button[data-testid=’login-button‘]“)
da data- tags am meisten isoliert sind, und hier der tag sogar eindeutig als data-testid benannt wurde, nehmen wir dieses Attribut als Identifier.
Wir erstellen die Klasse LoginPage mit den 3 Methoden:
import {Page} from '@playwright/test'; (1)
export class LoginPage{ (2)
readonly page: Page; (3)
constructor(page: Page){ (4)
this.page = page;
}
async setUsername(username : string){
await this.page.locator("input[data-testid='contract_input']").clear(); (5)
await this.page.locator("input[data-testid='contract_input']").fill(username); (6)
}
async setPassword(password : string){
await this.page.locator("input[data-testid='password_input']").clear();
await this.page.locator("input[data-testid='password_input']").fill(password);
}
async clickLoginButton(){
await this.page.locator("button[data-testid='login_button']").click(); (7)
}
}
- (1) Das Objekt Page aus dem Module playwright/test wird importiert
- (2) Es wird die Klasse LoginPage definiert und exportiert
- (3) Es wird eine Variable page vom Type Page (Browserfenster Objekt aus Playwright) definiert
- (4) Der Konstruktor für die Klasse LoginPage wird definiert
- (5) Es werden alle Daten aus dem Inputfeld gelöscht, welche eventuell vom Browser automatisch eingetragen wurden (Best Practice)
- (6) Es wird der übergebene Username eingetragen
- (7) Der Button für den Login wird geklickt
TestContext
In Playwright müssen wir wie bei Selenium vor der Ausführung der Tests einen Testkontext erstellen, um Playwright mitzuteilen, was wir überhaupt wo und wie testen wollen.Es stehen uns verschiedene Konfigurationsmöglichkeiten für die Testausführung zur Verfügung, darunter unter anderem:
- Welchen Browser wollen wir nutzen?
- Soll der Test Headless (ohne Rendering der Darstellung im Browser) ausgeführt werden?
- Wollen wir den Test Mobile ausführen?
- uvm.
Wichtig im Kontext von Playwright ist, dass wir hier einstellen können, dass Playwright die Aufnahme unseres Testfalls für den Playwright Trace Viewer erstellt.
Da im TestContext Hooks aus Cucumber genutzt werden, bietet es sich an, der Konvention zu folgen, diese in einem Ordner hooks abzulegen. Wir erstellen also einen Ordner hooks auf der Projektordnerebene und in diesem eine Datei TestContext.ts, welche wie folgt aussieht:
import { After, Before, setDefaultTimeout } from "@cucumber/cucumber"; (1)
import { Browser, chromium, Page, BrowserContext } from "@playwright/test"; (2)
let browser: Browser; (3)
let context: BrowserContext;
let page: Page;
setDefaultTimeout(60000); (4)
Before(async () => { (5)
try{
browser = await chromium.launch({ headless: false}); (6)
context = await browser.newContext({
recordVideo: { (7)
dir: "test-results/videos",
},
});
await context.tracing.start({ (8)
sources: true,
screenshots: true, snapshots: true
});
page = await context.newPage(); (9)
console.log('\n');
}
catch(error) {
console.log('test')
throw new Error('');
}
return page;
});
After(async () => { (10)
const random = Date.now() + Math.random(); (11)
await context.tracing.stop({ path: tracePath }); (12)
await context.close();
await browser.close();
});
export {page} (13)
- (1) Die Metatags Before und After sowie setDefaultTimeout werden aus dem Modul @cucumber/cucumber importiert
- (2) Die wichtigen Elemente aus dem Modul playwright/test die für die Tests nötig sind werden importiert
- (3) Es werden die essentiellen Objekte aus Playwright als Variablen deklariert
- (4) Diese Einstellung definiert, wie lange auf die asynchronen Aufrufe gewartet werden soll
- (5) Die Funktion Before wird erstellt, welche vor jedem Ablauf eines Testfalls, in unserem Fall vor jeder Ausführung der Features pro Datensatz ausgeführt wird
- (6) Es wird Chrome als Browser festgelegt, welcher nicht Headless laufen soll
- (7) Der Kontext unter welchem der Test laufen sollen wird festgelegt, hier kann man unter anderem festlegen, ob ein Video der Testausführung aufgezeichnet werden soll.
- (8) Dieser Punkt ist für den Trace Viewer wichtig, da wir die Tests über die Cucumber.js aufrufen, muss hier manuell festgelegt werden, dass die Testausführung für den Trace Viewer aufgezeichnet werden soll.
- (9) Das page Objekt von Playwright, welches für die Ausführung der Playwright Befehle benötigt wird, wird initialisiert
- (10) Die Funktion After wird erstellt, welche nach jedem Ablauf eines Testfalls, in unserem Fall nach jeder Ausführung der Features pro Datensatz
- (11) Es wird ein Name für die Tracefiles generiert, welcher aus dem aktuellen Datum und einer zufälligen Nummer besteht
- (12) Hier wird der Pfad angegeben, in welchem die Trace Dateien für den Trace Viewer hinterlegt werden soll (Default: ./test-results/trace)
- (13) Das page Objekt wird exportiert, damit es später in den Tests importiert und genutzt werden kann
Stepfiles
Mit unserem PageObject LoginPage haben wir nun die Grundlage um unsere Steps umzusetzen.
Im Ordner pages erstellen wir eine Datei mit dem Namen: LoginPage.steps.ts.
Wir müssen dem cypress-cucumber-preprocessor mitteilen welche Funktionen er auf welche Steps, in den Feature Files abbilden muss, deshalb ist das .steps als Teil des Dateinamen wichtig.
Die Umsetzung gelingt in Playwright wie folgt:
import { Given, When, Then} from '@cucumber/cucumber'; (1)
import { LoginPage } from '../pages/LoginPage'; (2)
import { page } from '../hooks/TestContext'; (3)
import { expect } from '@playwright/test'; (4)
/* Given */
Given('Der Nutzer ist auf der Startseite', async () => { (5)
await page.goto("http://testorbit.germanywestcentral.cloudapp.azure.com:4200");
});
/* When */
When('Der Nutzer gibt korrekte Daten für {string} und {string} ein', async function (username : string, password : string) { (6)
let loginPage = new LoginPage(page); (7)
await loginPage.setUsername(username);
await loginPage.setPassword(password);
await loginPage.clickLoginButton();
});
/* Then */
Then('Der Nutzer wird korrekt eingeloggt', async () => { (8)
await expect(page).toHaveTitle("BankingSoftwareFrontEnd")
});
- (1) Es werden die Gherkinbefehle aus Cucumber importiert
- (2) Die erstellte LoginPage wird importiert
- (3) Das im TestContext initialisierte page Objekt wird importiert
- (4) Die Assertion expect aus Playwright wird importiert
- (5) Der Step Given ‚Der Nutzer ist auf der Startseite‘ wird implementiert, es wird auf die Startseite navigiert
- (6) Der Step When ‚Der Nutzer gibt korrekte Daten für {string} und {string} ein‘ , dabei werden Nutzername und Passwort eingegeben und anschließend Login geklickt
- (7) Eine Instanz unserer Klasse LoginPage wird initialisiert um sie im Step nutzen zu können
- (8) Der Step Then ‚Der Nutzer wird korrekt eingeloggt‘, dabei wird überprüft, dass auf die Mainpage navigiert werden konnte und der Titel korrekt angezeigt wird.
Ausführung und Abschluss
Wir können nun unsere Feature Files als Tests ausführen, indem wir unser Script ablaufen lassen über den vorher von uns definierten Befehl:
npm test
Der Browser öffnet sich, der Test wird durchgeführt und wir bekommen die folgende Ausgabe im Terminal:
> test
> npx cucumber-js -p test_runner (1)
..... (2)
1 scenarios (1 passed) (3)
3 steps (3 passed)
1m08.245s (executing steps: 1m08.122s)
- (1) Es wird das Script test aus der package.json im Projektordner aufgerufen.
- (2) Die Punkte sind die Ausgabe des Fortschrittes, die Formatierung wurde in der „cucumber.js“ definiert.
- (3) Die Anzahl der Szenarios und Steps die durchgeführt worden, hier alle erfolgreich.
Trace Viewer
In der Datei „TestContext.ts“ haben wir definiert, dass für jeden unserer Testausführungen eine zip Datei mit den Trace Daten angelegt werden soll, damit wir diese anschließend mit dem Trace Viewer betrachten können.
Im Ordner „./test-results/trace“ im Projektordner finden wir die zip Dateien und können diese mit dem Befehl:
npx playwright show-trace .\test-results\trace\[Datei.zip]
durch den Trace Viewer ausführen lassen, wobei [Datei.zip] für den Namen der Tracedatei steht.
Es öffnet sich ein neues TraceViewer Fenster, in welchem wir die Durchführung unseres ausgewählten Tests betrachten können.
Das sieht dann wie folgt aus:
Herzlichen Glückwunsch, unsere ersten Tests mit Playwright und Cucumber waren erfolgreich!