Unit-Testing Angular 5 Apps mit Karma und Jasmine (Part 1)

Marcel Ploch
coodoo
Published in
7 min readJan 13, 2018

--

Moderne Web-Anwendungen werden immer komplexer und verwenden immer mehr JavaScript. Auch durch die die Nutzung von Frameworks wie Angular, React oder Vue verändert sich der Komplexität der Anwendung im Frontend deutlich. Gerade dadurch das JavaScript keine kompilierende Sprache ist, sondern der Code zur Laufzeit ausgeführt wird, können sich leicht und schnell Fehler einschleichen und in der Weiterentwicklung Bugs entstehen die bereits abgenommen worden waren. Aber wie kann man sich davor schützen?

Eine Möglichkeit ist es sich schon während des Build Prozesses der Anwendung mit Tests abzusichern. Hier gibt es sowohl Unit- als auch Integrations- oder End-to-End-Tests mit denen sich der Code regelmäßig prüfen und sichern lässt.

Im den folgenden Beiträgen wollen wir uns explizit mit dem Unit-Testing von Angular Anwendungen beschäftigen. Hier wollen wir uns anschauen wie wir das Projekt Setup so gestalten können, dass wir das meiste aus unserem Build Prozess holen können und unsere Anwendung so sichern können, dass Sie auch im bei Erweiterungen, Refactoring oder anderen Dingen immer sicher bleibt. Schon seit Angular 1.x sind Karma und Jasmine mit an Board und haben sich schon dort bewährt.

Alle Sourcen zu den einzelnen Parts werden auf GitHub veröffentlicht und sind hier zu finden:

Ich werde für jeden Part einen Tag anlegen so das man sich den Stand zum Part direkt beziehen kann.

Karma

Karma ist die Laufzeitumgebung für die Tests. Hier werden alle Abhängigkeit der Anwendung geladen und kompiliert und ausgeführt.
https://karma-runner.github.io/

Jasmine

Jasmine ist unser Test Framework mit dem wir Tests schreiben, Assertions ausführen und Services sowie Funktionen mocken werden.
https://jasmine.github.io/

First Step Project Setup

Als erstes installieren wir die Angular CLI welches uns dann einen Grundprojekt zur Verfügung stellt.

Wir wechseln jetzt in ein neues Verzeichnis und starten mit

ng new angularUnitTest

nach einer längeren Installationszeit finden wir folgende Verzeichnisstruktur wieder.

Verzeichnisstruktur nach der Installation

Nun können wir mit dem Befehl ng test die Tests ausführen die uns die CLI nach der Installation des Projektes mitgeliefert hat. Hier dürfen wir uns nicht wundern, den es wird ein Chrome Browser geöffnet in dem die Tests ausgeführt werden. Die Ausgabe im Chrome sollte dann wie folgt aussehen:

Erfolgreicher Test

Mit dem Befehl ng test versetzten wir Karma in den watch modus, so dass jede Änderung am Code direkt zu einem Durchlauf der Tests führt. Wenn wir nun zu der Datei app.component.spec.ts navigieren, welche im App Verzeichnis vorhanden ist, und diese öffnen, sehen wir die Beschreibung des Tests. In Zeile 25 sollte folgender Code Stehen:

expect(compiled.querySelector(‘h1’).textContent).toContain(‘Welcome to app!’);

Wenn wir den String von “Welcome to app!” in “Hello World” ändern sollten wir folgendes Bild erhalten.

Test failure.

Der Test schlägt fehl und wie sehen genau an welcher Stelle im Code der Fehler entsteht. Zunächst gehen wir wieder zum Ausgangscode zurück und speichern die Datei erneut. Wenn wir die Tests nicht bei jeder Code Änderung ausführen wollen sondern nur einmalig, können wir das mit dem Befehl ng test — single-run=true erreichen. Die Ausgabe in der Console lautet dann wie folgt:

Erfolgreiche Tests

Nun könnten wir mit dem Coden unserer App beginnen und dazu parallel immer wieder die Tests ausführen und haben gleichzeitig ein gutes Gefühl wenn diese immer grün sind.

Tests mit PhantomJS headless Browser ausführen

Mit der aktuellen Konfiguration wird es aber schwer auf einem Continues Integration System Tests auszuführen, da diese ja im Chrome ausgeführt werden.

Mit dem Befehl ng test — browsers=phantomjs könnten wir die tests auch im bekannten headless Browser PhantomJS ausführen. Leider streikt hier unsere Karma Konfiguration, da diese dafür aktuell nicht vorgesehen ist.

Error when using PhantomJS

Aber auch hierfür gibt es eine einfache Lösung.

Mit dem Befehl: npm install karma-phantomjs-launcher — save-dev können wir einfach das PhantomJS Karma Launcher Plugin installieren und diesen dann auch nutzen. Hierzu müssen wir noch Änderungen an der karma.conf.js im root Verzeichnis vornehmen:

Wenn wir nun ng test ausführen sollten wir zwar sehen das kein Chrome mehr gestartet wird, es aber Fehler hagelt:

Da PhantomJS ein headless Browser ist verfügt er nicht über alle neuen JS Erweiterungen über die auch Chrome verfügt. Deswegen navigieren wir in zur Datei polyfills.ts und kommentieren alle core-js polyfills wieder aus. Im Anschluss sollte unsere Ausgabe wie folgt aussehen.

Nun können wir unsere Test auch einfach auf unserem CI ausführen und merken bei jedem Commit ob die Anwendung noch läuft.

Metriken, Plugins und Beispielprojekt auf GitHub

Auch können wir mit der Methode weitere Metriken über unsere Anwendung erstellen. Gerne lässt man sich ja auch anzeigen wieviel Prozent Code durch Tests abgedeckt sind. Mit dem Befehl: ng test — code-coverage erstellt uns Istanbul im dem Verzeichnis coverage eine schön lesbare HTML Ansicht unsere Testergebnisse.

Da ich gerne auch noch andere Plugins und Erweiterungen für Karma nutze habe ich für uns eine eigene Config geschrieben, welche bei uns zum Einsatz kommt. Hier wird die Coverage direkt ins den Terminal geschrieben und die Darstellung der der einzelnen Tests ist einfacher und übersichtlicher.

Die passende Konfiguration und alle Änderungen sowie das Basis Projekt ist auf GitHub zu finden und kann von dort aus Schritt für Schritt ausgeführt werden.

Testing

Nun zu unserem ersten Test. Aber bevor wir mit dem Schreiben beginnen vorab noch ein Hinweis. Wer jetzt seine mit der Angular CLI erstellte und gewachsene Anwendung mal schnell durch ng test laufen lassen möchte wird schnell merken, dass es hier zu vielen Fehlern kommen wird. Dies liegt daran, dass die Angular CLI beim Erstellen von neunen Komponenten, Service usw. auch immer direkt einen Test Datei anlegt. Diese sind an dem Suffix spec.ts zu erkennen. Wer hier nicht regelmäßig alle Dependencies nachzieht wird spätestens jetzt eine Menge an Zeit investieren müssen um diese dort zu korrigieren. Wenn dies aber geschehen ist, ist die Grundlage geschaffen um dauerhaft Tests ausführen zu können.

Kehren wir nun zurück zu dem von der Angular CLI erstellten Test.

Jasmine setzt auf Behavior Driven Testing (BDD). Dies lässt sich am einfachsten am Aufbau der Test Datei erklären:

Jeder Test beginnt mit der Zeile

describe(“Test Name / Klasse”, () => {});

Im inneren der anonymen Funktion oder des Closures wird der Test beschrieben. Hier gibt es diverse von Jasmine bereitgestellte Hooks und Funktionen. In diesem fall benötigen wird noch den beforeEach Hook, welcher vor jedem Test ausgeführt wird. In diesem wird die für unseren Test wichtige Initialisierung der Komponente vorgenommen und sie für den Test bekannt gemacht (Zeile 7–11). Jeder Test beginnt dann mit der it-Funktion. Der erste Parameter beschreibt was im Test getestet werden soll und wird auch später in unserem Terminal ausgegeben. Die expect Funktion dient unsere Erwartungshaltung und der Vergleich den wir damit erstellen wollen. Hierbei Liefert Jasmine schon eine Menge an Assertions mit.

Wenn wir hier in Zeile 28 wieder aus “Welcome to app!” “Hello World!” machen, müssen wir das auch in der app.component.html anpassen.

Wenn beide Änderungen durchgeführt sind können wir mit ng test wieder unsere Tests ausführen und sehen, dass diese dann immer noch grün sind.

Fazit

Zusammenfassend haben wir nun mit dem Testing mit Angular 5 + Karma + Jasmine begonnen. Aller Einstieg ist schwer und man wird sehen, dass das Testen sicherlich auch Zeit kostet aber den Vorteil bietet bei einigen Dingen schnell und einfach zu sehen, ob Bugs oder Probleme zur Laufzeit entstehen können, welche man vorher vielleicht nicht betrachtet hat. Das Testen kostet Disziplin und Zeit, welche man, vor allem die Zeit, nicht immer hat. Doch wenn man sich einmal damit auseinander setzt und Bugs sucht, die auf dem Ersten Blick nicht ersichtlich sind, lernt man die Tests schnell zu schätzen, da diese schon Frühzeitig zeigen ob Änderungen Fehler im bisherigen Code verursachen.

Im nächsten Teil werden wir anhand einer ToDo Anwendung die verschiedenen Komponenten einer Angular Anwendung (Services, Component, Pipe, …) testen, als auch mit dem Mocking von Komponenten beginnen.

Part 1: Unit-Testing Angular 5 Apps mit Karma und Jasmine (Part 1)

Part 2: Testing von Componenten und Services

Part 3: Testing von Pipelines und Routen

Part 4: Interaktion mit Komponenten

Über Anregungen und Kommentare freue ich mich. Und nun Happy Coding.

--

--