Angular 2 testen mit Protractor (Update)

Protractor ist das Framework um Angular JS Anwendungen in einem End-To-End Test unter die Lupe zu nehmen. Zwar gibt es für Angular2 noch keine angepasste Version, sodass wir auf einige Features verzichten müssen, trotzdem ist auch Angular2 Bereit zum Test.

angular2_protractor2

Die Anforderung ist klar: wir wollen unsere Anwendung im Browser laufen lassen und Benutzer-Interaktionen simulieren um dann das Ergebnis zu prüfen. Unter der Haube von Protractor setzt auch dieses Framework dafür auf die bewährte WebDriver-API von Selenium. Selenium dient hier als Bindeglied zwischen unserem Test-Code und unserer Anwendung die im Browser abläuft. Die eigentlichen Tests werden schließlich mithilfe des Test-Frameworks „Jasmine“ zum Leben erweckt.

Projektsetup

Aber machen wir einen Schritt nach dem anderen und beginnen bei der Vorbereitung unseres Projektes. Zu allererst benötigen wir natürlich Protractor das mithilfe von npm schnell installiert ist:

 npm install –save-dev protractor

anschließend benötigen wir noch den bereits angesprochenen Selenium-Server der bei unseren Tests die Kommunikation zwischen dem Test-Code und den gewünschten Browsern übernimmt:

./node_modules/protractor/bin/webdriver-manager update

Für unser kleines Beispielprojekt verwenden wir Webpack als Bundler für unsere Webanwendung. Die Verwendung von Protractor ist aber nicht daran gebunden. Im Kern geht es darum eine laufende Anwendung bereit zu stellen, die protractor testen kann, ob die Anwendung mit Webpack,Browserfy oder Gulp/Grunt erzeugt wird spielt für unseren Test erst einmal keine rolle.

In unserem eigentlichen Task „test“ referenzieren wir ein Config-File das Protractor zur Initialisierung dient. In diesem legen wir fest unter welcher URL unsere Anwendung zur Verfügung steht, in welchem Ordner unsere Tests zu finden sind und welchen TestRunner (Jasmin) wir verwenden wollen

exports.config = {
  baseUrl: 'http://localhost:4300/',

  specs: [
    './target/**/*.test.js'
  ],
  exclude: [],

  framework: 'jasmine2',

  allScriptsTimeout: 110000,

  jasmineNodeOpts: {
    showTiming: true,
    showColors: true,
    isVerbose: false,
    includeStackTrace: false,
    defaultTimeoutInterval: 400000
  },
  directConnect: true,

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

  onPrepare: function () {
    var SpecReporter = require('jasmine-spec-reporter');
    jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: true}));
    
    
    browser.ignoreSynchronization = false;
  },


  /**
   * Angular 2 configuration
   *
   * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching
   * `rootEl`
   *
   */
  useAllAngular2AppRoots: true
};

test/protractor.conf.js

Um unsere Tests aus zu führen sorgen wir für ein clean unserer kompilierten Testsourcen, führen TypeScript aus (um aus unseren TypeScript tests JavaScript-Dateien zu erzeugen) und führen anschließen Protractor aus:

rimraf ./test/target && tsc ./test/**/*.ts -t ES5 –outDir ./test/target && protractor test/protractor.conf.js

Zum Test bitte

In unseren eigentlichen Tests interagieren wir nun über den Selenium Server mit der laufenden Anwendung. Wir rufen also die zu testende URL auf, führen Aktionen aus und prüfen die Anzeige. Für das eigentliche Auffinden der Elemente stehen uns diverse so genannte Locators zu Verfügung die Elemente über Tag-Names, CSS, Texte und IDs ermittelt werden können. Wer schon Erfahrung mit dem Testen von Angular 1 hat wird allerdings einige Selektoren vermissen, so gab es z.B. die Möglich Selektionen aufgrund von NG-Model-Bindings zu ermitteln. Diese Angular-Spezifischen Selektoren werden leider (noch?) nicht Unterstützt und stehen somit erst einmal nicht zur Verfügung.

Zum auffinden der Elemente bleiben also die nicht Anguluar-Spezifischen Selektoren:

describe“ um eine Test-Suite zu deklarieren um unsere Test zu gruppieren
beforeEach“ um in diesem Beispiel vor jeder Funktion eine frische Navigation auf unsere Seite durch zu führen
it“ um einen einzelnen Test zu deklarieren
expect“ um ein erwartetes Verhalten fest zu legen
Protractor liefert die entsprechenden Möglichkeiten Elemente auf zu finden
element“ dient zum Finden von Elementen
by.id“ Selector für die Suche nach ID
getText()“ Aktion die den Text des gefunden Elementes liefert

Ein wichtiger Punkt ist das alle Aktionen die mit/auf dem DOM des Browsers ausgeführt werden asynchron sind und damit einer so genannten Promise zurück liefern. Die „expect“ Methode akzeptiert solche Promises und wartet mit der Auswertung bis das Ergebnis der asynchronen Anfrage geliefert wurde. Wollen wir jedoch selber mit dem Ergebnis einer solchen Aktion weiter arbeiten ist es notwendig eine entsprechende Callback-Methode zu implementieren, die erst dann ausgeführt wird, wenn die Aktion abgeschlossen wurde.

Wer seine Anwendung mittels TypeScript schreibt wird vermutlich auch die entsprechenden Test in dieser Form verfassen wollen. Die dafür benötigten Type Definitions liefert uns das npm – Modul „typings“ (npm install –g typings) mittels:

typings install --ambient --save selenium-webdriver
typings install --ambient --save angular-protractor
typings install --ambient --save jasmine

Ein entsprechender Test könnte dann so aussehen:

/// <reference path="../../typings/index.d.ts" />

class PageModel {
    static get messageButton() {
        return element.all(by.className('btn-primary')).first();
    }
    
    static get errorButton() {
        return element.all(by.className('btn-primary')).get(1);
    }
    
    static get nfoPanel(){
        return element(by.className('nfoPanel'));
    }
    
    static get error(){
        return element(by.id("toasty"));
    }
}

describe("Home-Page", () => {
    beforeEach(() => {
        browser.get('http://localhost:4300/#hello');
    })

    it("MessageButton", () => {
        RootPageModel.waitUntilSpinnerFinished();
        expect(PageModel.nfoPanel.isPresent()).toBeFalsy();
        expect(PageModel.messageButton.isPresent()).toBeTruthy();
        expect(PageModel.messageButton.getText()).toEqual("Hello from Angular-InMemoryApi");
        
        PageModel.messageButton.click();
        expect(PageModel.nfoPanel.isPresent()).toBeTruthy();
    })

    it("ErrorButton", () => {
        RootPageModel.waitUntilSpinnerFinished();
        expect(PageModel.errorButton.isPresent()).toBeTruthy();
        PageModel.errorButton.click()
        expect(PageModel.error.isPresent()).toBeTruthy();
    })
})


Wie wir sehen konnten ist das Testen mittels Protractor nicht schwer, dank Jasmin, Selenium und Webpack ist ein solcher Prozess schnell aufgebaut und die ersten Tests implementiert. Neben den hier gezeigten Möglichkeiten bietet Protractor und Jasmin noch einige weitere Features wie das Generieren von Reports oder sogar das Erstellen von Screenshots nach jedem Test. Mehr dazu finden Sie auf den entsprechenden Seiten der Hersteller.

Beispiel Projekt:

https://github.com/dominikmathmann/angular-webpack-starter

Advertisements

Über Dominik Mathmann
Dominik Mathmann arbeitet als Berater, Entwickler und Trainer bei der GEDOPLAN GmbH. Auf Basis seiner langjährigen Erfahrung in der Implementierung von Java-EE-Anwendungen leitet er Seminare, hält Vorträge und unterstützt Kunden bei der Konzeption und Realisierung von Webanwendungen vom Backend bis zum Frontend. Sein derzeitiger Schwerpunkt liegt dabei auf der Entwicklung von JSF-Anwendungen. Er sieht die stärker werdende Position von JavaScript-Frameworks jedoch positiv und beschäftigt sich darüber hinaus mit Webframeworks wie AngularJS.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: