Bisherige Timeline

Mit dem Release Blog Post zu Angular 12, hatte das Angular Entwicklerteam bekannt gegeben das Support- / Entwicklungsende von Protractor zu evaluieren.
Nach einem Request for Comments wurde dann entschieden, die Entwicklung von Protractor mit dem Angular 15 Release Ende 2022 einzustellen. Die Zukunft für Teams, die auf Protractor unbedingt angewiesen sind, blieb ungeklärt.

Diese Nachrichten bedeuteten für unzählige Automatisierungsteams weltweit Recherche- und Handlungsbedarf,
Alternativen mussten gesucht werden und umfangreiche Migrationen waren einzuplanen.

Alternative Frameworks

Mit dem Release von Angular 12 wurde frühzeitig Support von weiteren Frameworks realisiert.
Neben Nightwatch, Cypress und WebdriverIO hat auch Playwright mit einem Migration Guide für Zukunftsfähigkeit gesorgt.

Hierbei hat sich Cypress laut npm trends als der populärste „Ersatz“ für Protractor positioniert.

Breaking News

Bis hierhin sind das alles altbekannte Fakten, wozu also dieser Artikel?
Seit Mitte Q3 2022 gibt es endlich ein Update zu dieser für viele QA Kollegen kritischen Frage.

 

Das vorläufige Support-Ende von Protractor ist verschoben!

 

In einem offiziellen Statement wurde bekannt gegeben, dass uns der Protractor Support noch ein weiteres Angular Major Release erhalten bleibt.
Die neue Timeline sieht vor, dass Protractor nicht wie bisher geplant Ende des Jahres 2022 deprecated wird, sondern erst mit Angular 16 im Sommer 2023 sein Lebensende erreicht.
Bis dahin beschränkt sich die Weiterentwicklung allerdings auf sicherheitsrelevante Themen und Browserkompatibilitäten.

Dies wird den vielen Projekten mehr Luft zur Migration geben und sorgt für ein wenig Entspannung in den betroffenen Teams.

Das endgültige Ende von Protractor?

Breaking News Teil 2

Eine weitere äußerst positive Nachricht wurde zeitgleich veröffentlicht.
Die Angular / Protractor Entwickler planen zusammen mit HeroDevs einen public fork, um das langfristige Überleben / den Support von Protractor zu sichern.

Angular Applikationen auf Ebene von Unit Tests und Integrationstests durchzutesten, ist eine relativ bequeme Geschichte. Gerüchten zu Folge wurde Angular bei Google von einem Test Team entwickelt und so ist die Testbarkeit der Applikation sozusagen direkt in der DNA der Architektur integriert.
Aber wie sieht es mit den E2E Tests aus? Wie werden Cross-Browser Tests im Rahmen einer Angular Applikation realisiert? Dieses Tutorial wird diese Punkte beleuchten.

Einrichtung der Umgebung

Als erstes sollten wir uns die Entwicklungsumgebung einrichten, Angular installieren, uns ein neues Angular Projekt erstellen und uns ansehen, welche Möglichkeiten zur Qualitätssicherung Angular von Haus aus mitbringt und wie speziell die E2E UI Testskripte aufgebaut sind.

Entwicklungsumgebung

Am sinnvollsten ist es, den Code der Applikation in einer IDE (Integrated Development Environment) zu öffnen. Eine IDE ist zwar nicht zwingend notwendig, um mit Angular oder automatisierten Tests zu arbeiten, bietet aber einige Vorteile im Vergleich zu einem simplen Editor (wie Notepad++), z.B. integrierte Git Anbindung, Organisation des Codes und der dazugehörigen Pakete, integrierte Autovervollständigung (Intellisense), integrierter Terminal (Batch / Powershell) und vieles mehr.  Solltest du mit Windows, Mac oder Linux arbeiten, eignet sich die Open Source Entwicklungsumgebung Visual Studio Code gut für diese Aufgabe, aber auch mit der kostenlosen Version von Visual Studio Community für Windows oder Mac, macht man nicht viel falsch. Ich verwende für dieses Tutorial Visual Studio Code.

Um Visual Studio Code zu installieren, lade es einfach  von der offiziellen Webseite runter, installiere und starte die Applikation: https://code.visualstudio.com/

Füge anschließend den Ordner „C:\tutorial\“ im Explorer zu dem Arbeitsbereich hinzu.

Angular Projekt in Visual Studio Code

Schon kannst du den Inhalt von dem Ordner und seine demnächst folgenden Source Dateien einsehen und bearbeiten. Ein weiterer Vorteil ist auch das integrierte Terminal unten, so können wir dort anschließend bequem Pakete installieren oder Tests starten:

  Tipp: Sollte Terminal nicht sichtbar sein, einfach „STRG+ö“ drücken bzw über das Menü View>Terminal auswählen.

Installation von Angular auf dem Rechner

Solltest du davor Angular noch nie auf deinem Rechner installiert haben, benötigst du zwei Sachen:

1.Intalliere Nodejs mit dem dazugehörigen NPM (Node Package Manager). Diesen findest du auf der offiziellen Webseite von NodeJS: https://nodejs.org/en/download/ – wichtig bei NodeJS ist natürlich die Installation vom npm package manager und das „Add to Path“, damit der npm Befehl aus dem Terminal bzw. der Konsole funktioniert

Node.JS Setup
Nodejs Installation

2.Installiere anschließend über NPM die Angular Client Applikation global auf deinem Rechner. Dazu kannst du nach der NodeJs Installation einfach folgende Anweisung in deiner Console bzw. in dem Visual Studio Code Terminal ausführen:

 npm install -g @angular/cli 

Erstellung des ersten Angular Projektes

Nach der Installation von Angular können wir unser erstes kleines Projekt erstellen. Dazu einfach den „ng new <projekt-name>“ aufrufen. Dieses habe ich bei mir project2 genannt.

 ng new project2 

Mit dem Projekt wird ein neuer Ordner mit der kompletten Angular Applikationsstruktur angelegt. Nach der Anlage ist das Angular Projekt sofort lauffähig. In der Ordnerstruktur wird dir vielleicht direkt unter dem Hauptverzeichnis bereits ein Ordner e2e Tests auffallen. Die Testdateien werden bei der Neuanlage eines Angular Projektes immer mit angelegt und enthalten bereits vorkonfigurierte Karma Unit Tests sowie fertig vorkonfigurierte Protractor Tests. Bequemer kann man Testautomatisierung einem Entwickler nicht näher bringen! 🙂

Du kannst auch sofort nach der Anlage aus dem Root Verzeichnis den Befehl „ng e2e“ bzw „npm run e2e“ ausführen.

 ng e2e 

Das Ergebnis sieht bei einem Erstprojekt etwa so aus:

Angular E2E Tests starten

Wie du schon mitbekommen hast, wurde auf dem Rechner die Angular Applikation gebaut, der Chrome Browser gestartet und die Applikation aufgerufen, außerdem wurde ein erster Check auf einen Titel durchgeführt. Der Aufruf ng e2e ist wirklich sehr bequem, da es uns eine Menge Arbeit abnimmt. Würden wir die gleiche Kette „manuell“ machen wollen, müssten wir folgende Aktionen unternehmen:

  1. Angular Applikation bauen und starten mit dem Befehl ng serve in einer eigenen Konsole
  2. Start vom Webdriver Manager mit webdriver-manager start in einer eigenen Konsole (evtl. sollte davor noch webdriver-manager update durchgeführt werden)
  3. Start der Testfälle mit dem Befehlt protractor e2e/protractor.conf.js in einer weiteren Konsole

Was die einzelnen Befehle bedeuten und welche weiteren Möglichkeiten existieren, Testfälle auszuführen, schauen wir uns etwas weiter unten an.

Struktur und Aufbau der E2E Tests in Angular

Der erste UI Test war ja schon erfolgreich, obwohl wir dazu auch noch überhaupt nichts beigetragen haben. Aus diesem Grund schauen wir uns jetzt die einzelnen Komponenten der E2E Testfälle an und wie diese in den Testskripten realisiert werden.

Als erstes schauen wir uns die beteiligten Komponenten an. Das Standard Testframework in Angular ist Jasmine. Aus diesem werden die Testfälle über das Testautomation Tool Protractor mit einem Selenium Webdriver im Hintergrund durchgeführt.

Jasmine

Jasmine ist ein sogenanntes Behavior-Driven Development Testing Framework, dass viele hilfreiche Funktionalitäten bereitstellt, die im Rahmen der Testdurchführung auf unterschiedlichen Schichten (Unit Tests / Integrationstests und auch E2E UI Tests) immer wieder benötigt werden, z.B. zur Verifizierung von Soll / Ist Ergebnissen.

Behavior Driven bedeutet, dass die Test Suiten und Test Cases so spezifiziert werden, dass sie das fachliche Soll-Verhalten der Applikation beschreiben, wodurch es auch für außenstehende einfacher ist, die Testinhalte und Ziele zu verstehen.

Der Aufbau einer typischen Jasmine „Test Suite“ sieht eigentlich immer so aus:

describe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});

Describe entspricht einer TestSuite und kann in sich mehrere it’s enthalten.
Ein it entspricht einem Testfall welches in sich eine Logik enthält, die z.B. prüft ob etwas dem erwarteten Wert entspricht
Die Struktur ist etwas gewöhnungsbedürftig, lässt sich aber nach einer Zeit tatsächlich ganz gut lesen.

Protractor

Protractor ist die offizielle UI Bibliothek für Angular UI Testfälle. Protractor ist eigentlich nichts anderes, als ein Wrapper für den Selenium Webdriver. Im Endeffekt benutzt man also Selenium zur Browserautomatisierung, mit dem Vorteil, die für Angular spezifischen Methoden von Protractor als Wrapperfunktionen nutzen zu können.

E2E Ordnerstruktur

Wenn wir uns den erstellten Ordner ansehen, finden wir darin folgende Dateien

C:\tutorial\project2\e2e\protractor.conf.js
C:\tutorial\project2\e2e\tsconfig.e2e.json
C:\tutorial\project2\e2e\src\app.e2e-spec.ts
C:\tutorial\project2\e2e\src\app.po.ts

Die tsconfig.e2e.json werden wir an dieser Stelle vorerst auslassen, da diese lediglich für die TypeScript Konfiguration zuständig ist (eine kurze Anleitung dazu kommt demnächst). Die anderen Dateien sollten wir uns aber detaillierter anschauen:

Testkonfigurationsdatei protractor.conf.js

Die Datei protractor.conf.js ist der Kern der E2E Automatisierung. Diese ist für die Konfiguration der Protractor Ausführung verantwortlich und definiert u.a. welche Testspezifikationen ausgeführt werden, z.B.:

specs: [
  './src/**/*.e2e-spec.ts'
],

Mit welchen Capabilities (Art des Browsers, Einstellungen des Browsers) z.B.:

capabilities: {
'browserName': 'chrome'
},

welche Jasmine Optionen gelten, welcher Reporter verwendet wird, welche Timeouts die Skripte enthalten und vieles vieles mehr. Eine genaue Auflistung der unterschiedlichen Möglichkeiten findest du unter: https://github.com/angular/protractor/blob/master/docs/api-overview.md#config-file

Diese Datei werden wir dann noch in weiteren Szenarien anpassen und erweitern.

Jasmine Testfallspezifikationsdatei src/app.e2e-spec.ts

Die Datei enthält die Jasmine Spezifikation der „Testsuiten“ und „Testfälle“. Der Verweis auf app.po ist interessant, weil dort die sogenannte Page Object Implementierung hinterlegt ist. Mehr dazu im nächsten Abschnitt

import { AppPage } from './app.po'; //Import von Page Object AppPage aus der datei src/app.po.ts
 
describe('workspace-project App', () => { // neue TestSuite
  let page: AppPage; //globale AppPage Variable, verfügbar für alle Its
 
  beforeEach(() => { //Jasmine "Init" Klasse, diese wird jedes mal ausgeführt, bevor ein 'it' ausgeführt wird
    page = new AppPage(); //Erstellung neuer Instanz der der Page Object Klasse AppPage
  });
 
  it('should display welcome message', async () => { //neuer Testfall
    await page.navigateTo(); //Aufruf einer Page Object Funktion, mit der zu der Startseite navigiert wird
    expect(await page.getParagraphText()).toEqual('Welcome to testapp!'); //überprüfung ob der Paragraph Text dem erwarteten Wert "Welcome to testapp!" entspricht
  });
});

Page Object Implementierung src/app.po.ts

Die Datei stellt das Page Object bzw. „funktionale Abstraktion“ der tatsächlichen Protractor Zugriffe auf die Elemente einer Webseite dar. In dieser werden also die einzelnen Elemente gesucht und über Protractorfunktionalitäten angesteuert.

import { browser, by, element } from 'protractor'; // importiert benötigte Funktionalitäten aus dem Protractor Modul

export class AppPage { // exportiert die Klasse, die von anderen Klassen konsumiert und genutzt werden kann
async navigateTo() { //Navigationsmethode
await browser.get('/'); //Ruft über protractor die "baseadress" Adresse auf, die in protractor.conf.js hinterlegt ist
}

async getParagraphText() {
await element(by.css('app-root h1')).getText(); //sucht mit Hilfe von css Identifier ein Element mit dem Tag h1 und gibt den Text von diesem Element zurück.
}
}

Aufbau des Angular Tutorial Projektes

Nachdem wir den grundsätzlich Aufbau eines E2E Tests angeschaut haben, möchten wir eine etwas komplexere Anwendung nehmen, mit der wir anschließend unsere Testfälle entwickeln und anschließend mit unterschiedlichen Konfigurationen testen werden. Dazu nehmen wir einfach das auf der Angular Hauptseite https://angular.io/tutorial/toh-pt0 aufgeführte Beispiel. Da es bei diesem Tutorial nicht um den Aufbau einer Angular Anwendung geht, laden wir einfach die fertige Angular Applikation von der Tutorialseite runter:

Links zu dem finalen Review findest du hier: https://angular.io/tutorial/toh-pt6#final-code-review
Und die Zip Datei kann hier bezogen werden: https://angular.io/generated/zips/toh-pt6/toh-pt6.zip

Solltest du die komplette Anwendung selbst aufbauen möchten, folge einfach dem sehr gut beschriebenen Tutorial auf der angular.io Webseite.

Ich habe für dieses Tutorial ein neues Verzeichnis auf C: mit dem Namen „tutorial“ angelegt, dorthin die zip Datei runtergeladen und anschließend die zip Datei in einen weiteren Unterordner mit der Bezeichnung „angular_heroes“ entpackt.

Anschließend wechseln wir in das Anwendungsverzeichnis und führen npm install aus, um die benötigten Pakete für unsere Angular Anwendung zu installieren:

 cd C:\tutorial\angular_heroes
npm install

Damit sollten sich die erforderlichen Pakete installieren lassen.  Am Ende müsste in etwa folgende  Meldung erscheinen:

 
added 1113 packages from 1279 contributors and audited 34260 packages in 70.961s
found 14 vulnerabilities (9 low, 5 high)
run `npm audit fix` to fix them, or `npm audit` for details
 

Tipp: Sollte npm install mal nicht beim ersten mal erfolgreich durchlaufen, einfach noch einmal probieren. Ab und zu gibt es da Probleme beim Download von phantomjs oder anderen Paketen.

Nun müssten wir die Applikation starten können, dazu einfach in den angular_heroes Ordner gehen und mit ng-serve starten:

ng serve --open

Mit dem –open Parameter wird die Applikation nach Start in dem Standard Browser geöffnet.

Nach diesem Befehl sollte sich dein Browser mit der URL http://localhost:4200/dashboard öffnen und du müsstest das im Angular Tutorial gezeigte „Dashboard“ sehen. Mach Dich ruhig ein paar Minuten mit der Applikation vertraut, bevor wir mit der automatisierten Qualitätssicherung dieser Applikation loslegen.

Wichtig: Da die Angular Leute ja bekannt für ihre Begeisterung für testbare Anwendungen / Testautomatisierung sind, haben sie natürlich schon etliche Testfälle für das Tutorial Projekt hinzugefügt. Du kannst dir natürlich gerne die E2E Testfälle der Entwickler anschauen und diese auch gerne mit npm run e2e ausprobieren. Ich würde allerdings kurz bitten den Ordner „src“ zu löschen bzw. außerhalb der Applikation zu verschieben und einen neuen, leeren Ordner src anzulegen, damit wir die Testfälle gleich komplett neu in der Page Objects Struktur aufbauen können.

Aufbau der UI Testautomatisierung für das Angular Projekt

Da wir nun ein funktionierendes „System Under Test“ haben, können wir uns überlegen, welche Testfälle dafür relevant sind. Am besten geht das natürlich direkt in der Jasmine BDD Notation. Daher starten wir hier einfach mit dem „Verhalten“ in dem Spec File.

Erstellung der Jasmine Spec File(s)

Wir legen uns in dem Ordner src eine Datei an. Im Visual Studio Code klicke einfach rechts auf den Ordner im Explorer und wähle „New File“ aus.

Wir betrachten einfach die beiden Views der Appliaktion und unterteilen sie in „Verhalten / Eigenschaften, die wir Testen möchten.

Beim Dashboard könnten wir z.B. prüfen ob:

  • der Titel „Tour of Heroes“ ist
  • die 4 Top Heroes angezeigt werden
  • die Navigation verfügbar ist und aus Dashboard und Heroes besteht

Bei der Hero View könnten wir prüfen ob:

  • Die Liste der Helden existiert
  • wir einen neuen Helden anlegen können
  • wir einen Helden löschen können

Die dazugehörige Jasmine Struktur könnte daher so aussehen:

describe('Heroes Dashboard', ()=> {
    it(' should have "Tour of Heroes" as title',()=> {
 
    });
    it(' should contain 4 Top Heroes',()=> {
 
    });
 
    it(' should have a navigation',()=> {
 
    });
 
});
 
describe('Heroes ', ()=&amp;gt;{
    it(' view contains a list of heroes',()=> {
 
    });
 
    it(' creation is possible',()=> {
 
    });
 
    it(' deletion is possible',()=> {
 
    });
});

Wenn wir jetzt npm run e2e ausführen, sollte die Ausgabe so aussehen:

Jasmine started
Heroes Dashboard
√  should have "Tour of Heroes" as title
√  should contain 4 Top Heroes
√  should have a navigation

Heroes
√  view contains a list of heroes
√  creation is possible
√  deletion is possible
Executed 6 of 6 specs SUCCESS in 0.023 sec.

Die Testfälle machen an der Stelle natürlich überhaupt nichts. Aber wir sehen schon mal, dass Jasmine mit unserer Struktur arbeiten und die „it“ Anweisungsblöcke problemlos aufrufen kann.

Erstellung der dazugehörigen PageObjects

Die Page Objects Dateien sollten Funktionen und Hilfsfunktionen der Businesslogik einer View abbilden. Deshalb legen wir uns nun eine weitere Datei in das Verzeichnis e2e/src an, die wir  dashboard.po.ts nennen. Ich werde die verwendeten Funktionen mit den dazugehörigen Links zu der Protractor Hilfe versehen, damit du dir gleich die offizielle Beschreibung ansehen kannst inkl. der Rückgabewerte der Funktionalität und dir natürlich auch angewöhnst, bei neuen Funktionen die zugehörige Hilfe zu verwenden.

Page Objects Datei dashboard.po.ts

Um das Verhalten der View testen zu können, könnten wir z.B. in der dazugehörigen Page Object Datei folgende Funktionen anlegen:

  1. Zu der View navigieren
  2. Liste mit Top Helden zurückgeben
  3. Title auf dem Dashboard zurückgeben
  4. Die Namen der Navigationselemente als Array zurückgeben

Rückgabe vom Titel und die Navigation zur Seite ist an sich relativ einfach, wir könnten uns eigentlich komplett den beiden Funktionen navigateTo und getTitle aus dem neuen e2e Projekt bedienen.
Die navigateTo Methode ruft lediglich browser.get(‚/‘) auf, womit der Browser angewiesen wird, die baseAddress (aus protractor.conf.js) aufzurufen.
Die getTitle Methode sieht schon etwas komplizierter aus: dort wird mit element(by.css(‚app-root h1‘)) ein Element gesucht und mit getText() anschließend der innerText von diesem Element zurückgegeben.

Auch die beiden anderen Funktionen sehen ähnlich aus, nur verwenden wir dafür dann die Suche nach mehreren Elementen mit dem Befehl element.all. Dieser Befehl liefert uns eine Art Array zurück (zur Synchronisierungt kommen wir gleich), welches 0 bis n Elemente enthalten kann, die mit den Kriterien gefunden bzw nicht gefunden wurden. Bei der Liste mit den Navigationsnamen nutzen wir die gleiche element.all Funktion, nur bekommt diese am Ende noch die Map Funktion, die eben nur die Texte aus den Daten extrahiert und uns den dazugehörigen „Texte“ zurück gibt.


import { browser, by, element, ElementFinder } from 'protractor';
  
export class DashboardPage {
 async navigateTo() {
    await browser.get('/');
  }

 async getTopHeroes(){
    return await element.all(by.css('app-root app-dashboard > div h4'));
  }
  
  async getDashboardTitle() {
    return await element(by.css('app-root h1')).getText();
  }

  async getNavigationItemNames() {
    return await element.all(by.css('app-root nav a')).map((el: ElementFinder) =><strong>;</strong> el.getText()); 
  }
}

Page Objects Datei heroes.po.ts

Um die Funktionen der Heroes Sicht abdecken zu können, würden sich folgende Methoden anbieten:

  • Liste von den Heroes zurückgeben
  • Einen neuen Hero anlegen
  • Einen Hero löschen
import { browser, by, element, ElementFinder } from 'protractor'; 
  
export class HeroesPage { 
  async navigateTo() { 
    await browser.get('/heroes'); 
  }
 
  async getHeroList(){
    return await element.all(by.xpath('//app-heroes/ul'));
  }
  
  async addHero(name:string) { 
    await element(by.xpath('//app-heroes//input')).sendKeys(name);
    await element(by.xpath('//button[text()=" add "]')).click();
    return await element(by.xpath('//app-heroes/ul//*[contains(text(),"'+name+'")]'));
  }
 
  async deleteHero(name:string) { 
   await element(by.xpath('//app-heroes/ul//*[contains(text(),"'+name+'")]/../button[@title="delete hero"]'));
    return await element.all(by.xpath('//app-heroes/ul//*[contains(text(),"'+name+'")]'));
  }
}

Implementierung der automatisierten E2E Testfälle

Nachdem die Funktionalität implementiert ist, wollen wir diese aus unseren Jasmine Spec Testfällen aufzurufen. Aber erst mal wird es etwas theoretischer:

Asynchronität in der Testautomatisierung mit JavaScript / Protractor

Solltest du bisher mit Selenium in Java oder C# gearbeitet haben, sollte dir der Code sehr bekannt vorkommen. Der vertraute Eindruck täuscht an dieser Stelle aber. Wir arbeiten mit Javascript / Typescript und natürlich auch mit Protractor asynchron. Wie die Hilfe von element(by.css(‚app-root h1‘)) schon anmerkt, gibt die Suche einen sogenannten „ElementFinder“ und nicht ein WebElement wie in C# / Java zurück. Dieses kann zwar für die meisten Funktionen wie ein WebElement verwendet werden, enthält aber auch die Besonderheit, dass die Funktionen eben nicht synchron ausgeführt werden, sondern erst dann, wenn der ElementFinder das dazugehörige Element auch gefunden hat. Wenn du dir auch erneut z.B. getText() als Funktion anschaust, wirst du feststellen, dass diese keinen String Wert zurückliefert, sondern einen Promise, der „verspricht“ irgendwann einen String Wert zu liefern. In diesem Fall heißt es, dass wenn du z.B. den Wert von getText() in die Konsole ausgeben möchtest, dieser zwingend einen await bzw. .then zur Synchronisierung benötigt, z.B.:

await element(by.tagName('h1')).getText().then(function(wert){ console.log(wert); })

Das muss man im Hinterkopf behalten, funktioniert aber problemlos, wenn man etwas javascript / typescript Erfahrung mitbringt bzw. Interesse hat, sich in dieses Thema einzuarbeiten.

Spec File mit Aufrufen der Page Objects

Aber nun kommen wir zu der Spec Datei, diese würde nun am Ende so aussehen:

import { DashboardPage } from './dashboard.po';
import { HeroesPage } from './heroes.po';
 
 
describe('Heroes Dashboard', () => {
    let dashboardPage;
 
    beforeAll(() => {
        dashboardPage = new DashboardPage();
    });
    beforeEach(async () => {
        await dashboardPage.navigateTo();
    });
 
 
    it('should have "Tour of Heroes" as title', async () => {
        expect(await dashboardPage.getDashboardTitle()).toEqual("Tour of Heroes");
    });
 
    it('should contain 4 Top Heroes', async () => {
       await dashboardPage.getTopHeroes().then(function (arr) {
            expect (arr.length).toEqual(4);
        })
    });
 
    it('should have Dashboard and Heroes in Navigation List', async () => {
       await dashboardPage.getNavigationItemNames().then((arr) => {
            expect (arr.length).toEqual(2);
            expect (arr[0]).toEqual("Dashboard");
            expect (arr[1]).toEqual("Heroes");
        })
 
    });
 
});
 
describe('Heroes ', () => {
    let heroesPage;
 
    beforeAll(() => {
        heroesPage = new HeroesPage();
    });
 
    beforeEach(async() => {
        await heroesPage.navigateTo();
    });
 
 
    it('view contains a list of ten heroes', async () => {
       await heroesPage.getHeroList().then((arr) => {
            expect(arr.length).toEqual(10);
 
        })
    });
 
    it(' creation is possible', async () => {
        //neuen Helden anlegen
        let neuerHeld = heroesPage.addHero("SuperTester");
        //prüfen ob der Held neben einer neuen ID auch den richtigen Namen enthält
        expect(await neuerHeld.getText()).toContain("SuperTester");
 
        //Bonus: Asyonchronität, hier geben wir die Liste aller Helden aus:
        heroesPage.getHeroList().then((helden) =&amp;gt; {
            for (let held of helden) {
                held.getText().then((heldenname) =&amp;gt; {
                    console.log(heldenname);
                })
            }
        });
 
        //und hier die Demonstration der Asynchronität von Protractor:
        console.log("Dieser Text wird in der Console früher ausgegeben, als die Liste der Helden ;-)");
 
    });
 
    it(' deletion is possible', async () => {
        await heroesPage.deleteHero("SuperTester").then((arr) => {
            expect(arr.length).toEqual(0);
        })
 
    });
});

Damit sind unsere ersten automatisierten E2E Testfälle der Angular Applikation fertig.

Starte die Testdruchführung mit

 ng e2e 

und überprüfe die Testergebnisse.

Der Test-Code ist natürlich noch nicht perfekt und könnte noch weiter verfeinert werden. Er demonstriert aber ganz gut, wie die Testautomatisierung mit Protractor in einem Angular Umfeld grundsätzlich realisiert werden kann. Ich habe auch eine kleine Demo der Asynchronität in die Specs eingebaut, um zu verdeutlichen wie der „Executor“ vorgeht.

Zusammenfassung

In diesem Tutorial haben wir gelernt, wie man sehr schnell eine funktionierende state-of-the-art  E2E Testautomatisierungslösung einer Angular Applikation mit Hilfe der automatisch generierten Vorlage in einem Angular Projekt aufsetzen kann. In dieser Vorlage haben wir Jasmin Framework verwendet, um eine Testspezifikation mit Hilfe der behavior-driven Notation zu definieren. Anschliessend haben wir das selenium-basierte Testautomatisierungsframework Protractor verwendet, um die konkreten Aktion dieser Testspezifikation zu automatisieren. Am Schluss haben wir die integrierte Testlaufzeitumgebung von Angular verwendet, um bequem die Testdurchführung unserer Tstsuite mit einem einzigen Befehl zu starten.

Ich werde demnächst weitere Beiträge zu dem Thema Testautomatisierung im Angular Umfeld hinzufügen, u.A.

  • Durchführung von E2E Protractor Tests in der CI Pipeline
  • Durchführung der E2E Testfälle in der Cloud (z.B. mit BrowserStack)
  • Mocken von Rest API um isoliert Oberflächen testen zu können
  • und evtl. einiges mehr.

Schaue daher regelmäßig vorbei 😉


Hat dir dieses Tutorial gefallen? Fehlt noch etwas oder du kommst an einigen Stellen nicht weiter? Bitte teile uns dein Feedback in den Kommentaren mit.

Während der Automatisierung mit Jasmine / Protractor Framework ist es oft sinnvoll, die bestehenden expect Klassen (u.a. hier beschrieben: https://www.testautomatisierung.org/expect-jasmine-javascript-framework/) zu erweitern.

Dazu wird von dem Framework die addMatchers Methode bereitgestellt. In dieser können Funktionen ausimplementiert werden, die dann anschließend in gewohnter Weise mit Expect aufgerufen werden.

Ein sehr gutes Beispiel steht zu diesem Thema auf GitHub bereit:  https://gist.github.com/elgalu/94284ec0ac3e8a590507

Es wird gezeigt, wie mit Hilfe von expect() und der dazugehörigen Erweiterungsmethoden die Klassen eines Elements überprüft werden können.

 

 

Um im Jasmine Framework und natürlich allen darauf basierten Testautomatisierungs-Frameworks (z.B. Protractor für AngularJS) erwartetes Sollverhalten überprüfen zu können, kann die Funktion / das Keyword „expect“ verwendet werden. Mit den Funktionen können Zeichenketten, Objekte und andere Elemente überprüft werden. Weiterlesen