Wie testen Webentwickler immer komplexere Javascript-Anwendungen in vielen Browsern? Vor dieser Frage standen die Entwickler des Angular.js-Framework und schufen fix eine Software namens Karma.
Mit Karma [1] soll die Test-getriebene Entwicklung “leicht, schnell und vergnüglich” sein. Das Tool startet eine in Javascript geschriebene Webanwendung. Es führt dann die vom Entwickler vorgegebenen Tests automatisch in mehreren Browsern zugleich aus und protokolliert die Ergebnisse. Die Browser dürfen sogar auf externen Geräten wie einem Smartphone oder einem Tablet-PC laufen. So lässt sich der eigene Code schnell unter realen Bedingungen testen.
Für erste Tests zündet Karma bei Bedarf den Code auch in einer ohne GUI laufenden Phantom.js-Instanz (Headless-Betrieb). Karma überwacht zudem den Code und führt bei jeder Änderung automatisch die hinterlegten Tests neu aus.
Ausgeliefert
Karma selbst besteht aus mehreren Plugins, die jeweils eine ganz bestimmte Aufgabe lösen. So sorgt zum Beispiel das Plugin »karma-chrome-launcher« dafür, dass der Code innerhalb des Browsers Chrome startet.
In der Regel spannen die Plugins für ihre Aufgaben weitere Software ein. Auch die eigentlichen Tests delegiert Karma über passende Plugins an etablierte Test-Frameworks wie Jasmine [2], Mocha [3] oder Qunit [4]. Webentwickler müssen folglich nicht umlernen, sondern dürfen ihre vertraute Testumgebung nutzen. Über selbst geschriebene Plugins binden sie auch selbst gestrickte Test-Frameworks ein. Weitere Karma-Plugins unterstützen nach dem gleichen Prinzip Continuous Integration via Jenkins [5], Travis [6] oder Semaphore [7].
Nach seinem Start lädt Karma zunächst alle Plugins und liest seine Konfigurationsdatei ein. Dann startet es im Hintergrund einen Webserver, der die zu testende Webanwendung anbietet. Anschließend startet Karma die Browser mit der URL des Webservers.
Bei Bedarf steuert der Webentwickler den Webserver auch manuell per Browser an. Karma registriert diesen Schritt im Hintergrund und testet diese Browser automatisch mit.
Der Webserver liefert eine speziell präparierte Seite aus, die im Browser über Websockets mit dem Webserver kommuniziert. Die Seite enthält ein I-Frame, in das der Webserver eine weitere Unterseite injiziert. Sie enthält den zu testenden Code, die auszuführenden Tests sowie weiteren Code, der die eigentliche Testausführung steuert. Er ist Teil eines so genannten Adapter-Plugins, das die Tests an das zugehörige Test-Framework weiterreicht. Entscheidet sich der Webentwickler zum Beispiel für das Framework Jasmine, sorgt der Jasmine-Adapter dafür, dass diese Tests in den Browsern ablaufen.
Die Testergebnisse fließen über den Websocket zurück zum Server. Dort fangen sie Reporter-Plugins ab, die alle Resultate aufbereiten und in der gewünschten Darstellung zurückliefern. Im einfachsten Fall gibt sie der Reporter »progress« als Meldung auf der Kommandozeile aus.
Rufbereitschaft
Die Entwickler empfehlen Karma und alle nötigen Plugins in das jeweilige Projektverzeichnis der zu testenden Webanwendung zu installieren. Einzige Voraussetzung dazu sind Node.js und NPM. Die folgenden zwei Befehle holen Karma in das aktuelle Projektverzeichnis:
npm install karma --save-dev npm install karma-jasmine jasmine-core karma-chrome-launcher --save-dev
Der zweite Befehl lädt drei Plugins nach: Der Test-Adapter »karma-jasmine« erlaubt es, Jasmine als Test-Framework zu nutzen, »jasmine-core« holt das eigentliche Jasmine-Framework auf die Platte und »karma-chrome-launcher« sorgt noch dafür, dass Karma selbstständig Chrome starten kann. Die Pakete landen schließlich innerhalb des Projektverzeichnisses im Unterordner »node_modules«. Nach der Installation startet Karma über den Aufruf von:
./node_modules/karma/bin/karma
Um Tipparbeit zu sparen, kann der Entwickler noch global das Paket »karma-cli« installieren:
sudo npm install -g karma-cli
Es richtet das Kommandozeilen-Programm »karma« ein, das als Alias für den obigen Bandwurmbefehl fungiert und somit automatisch die aktuell verfügbare Karma-Fassung aufruft.
Fragwürdig
Damit weilt Karma auf der Festplatte. Es weiß aber noch nicht, was es wie und wo testen soll. Genau dies verrät dem Tool eine Konfigurationsdatei, die der Befehl »karma init karma.conf.js« erzeugt. Den Namen der Konfigurationsdatei wählt der Entwickler nach Gusto.
Wie die Endung andeutet, handelt es sich dabei um eine Javascript-Datei. Karma importiert sie einfach bei seinem Start und wertet die darin gesetzten Variablen aus. Alternativ lässt sich die Konfiguration auch in Coffeescript oder Typescript notieren. Dazu erhält die Konfigurationsdatei einfach die Endung ».coffee« beziehungsweise ».ts«.

Abbildung 1: In diesem Fall wird Karma das Jasmine-Framework verwenden, die Tests (nur) in Chrome ausführen, dabei alle Javascript-Dateien aus dem Ordner »js« einbinden und diese stetig auf Veränderungen überwachen.
In jedem Fall muss der Entwickler einem Assistenten ein paar Fragen zum Projekt beantworten (Abbildung 1). Dazu zählen insbesondere das zu verwendende Test-Framework und die zu nutzenden Browser. Den Speicherort der Quellcode-Dateien trägt er bei Bedarf auch später manuell nach.

Abbildung 2: Die Konfigurationsdatei nutzt Javascript-Code, den Karma bei seinem Start lädt. Sie listet zum Beispiel jene Dateien auf, die der Browser für seine Tests später laden muss.
Innerhalb der Konfigurationsdatei zeigt »module.export« auf eine Funktion, die ein Konfigurationsobjekt entgegennimmt. Die Funktion setzt dann im Objekt die passenden Variablen (Abbildung 2). Deren Bedeutung erläutern ausführliche Kommentare. Im folgenden Beispiel würde Karma alle Javascript-Dateien aus den Ordnern »js« und »tests« einladen:
files: [
'js/*.js',
'tests/*.js'
],
»files« listet dabei alle Dateien auf, die der Browser später laden muss. Dazu gehören auch sämtliche Tests. Platzhalter wie das Sternchen erschlagen gleich mehrere Dateien.
Browserwahl
Hinter »browsers« folgen alle Browser, in denen Karma den Code testen soll. Tabelle 1 verrät für die wichtigsten Browser, welche Kürzel in die Konfigurationsdatei gehören. Für auf diese Weise ergänzte Browser wartet in der Regel ein entsprechender Launcher. Für diesen muss der Webentwickler zusätzlich noch das passende Plugin holen.
|
Browser |
Eintrag in der Konfigurationsdatei |
Launcher für den Browser |
|---|---|---|
|
Chrome |
»Chrome« |
»karma-chrome-launcher« |
|
Firefox |
»Firefox« |
»karma-firefox-launcher« |
|
Phantom.js |
»Phantom.js« |
»karma-phantomjs-launcher« |
|
Opera |
»Opera Classic« (bis Version 12), »Opera« (ab Version 15) |
»karma-opera-launcher« |
|
Edge |
»Edge« |
»karma-edge-launcher« |
|
Internet Explorer |
»IE« |
»karma-ie-launcher« |
|
Safari |
»Safari« |
»karma-safari-launcher« |
Um beispielsweise den Code auch in Firefox zu testen, ergänzt der Entwickler »browsers: [‘Chrome’, ‘Firefox’],« in der Konfigurationsdatei und installiert dann noch über die Kommandozeile den passenden Launcher via:
npm install karma-firefox-launcher --save-dev
Für welchen Browser welches Plugin notwendig ist, verrät ebenfalls Tabelle 1. Viele weitere Launcher finden sich zudem unter [8]. Phantom.js eignet sich als Headless-Browser vor allem für erste schnelle Tests während der Entwicklung: Da er kein GUI startet, liefert er deutlich schneller Ergebnisse, die aber im Gegenzug nicht mit denen von Chrome, Firefox & Co. übereinstimmen müssen.
Webentwickler starten zudem jeden beliebigen anderen Browser, indem sie in der Konfigurationsdatei einfach den kompletten Pfad zu seiner Binärdatei angeben, etwa:
browsers: ['/usr/local/bin/Exotischer_Browser.sh'],
Daneben besteht die Option, einfach mit einem Browser den von Karma gestarteten Webserver anzusteuern. Auf diesem Weg testet der Entwickler auch die Browser von Mobilgeräten, Tablet-PCs und anderen Betriebssystemen.
Hinter »frameworks« folgen die zu verwendenden Test-Frameworks. Standardmäßig ist dies nur »jasmine«, sämtliche verfügbaren Frameworks finden sich unter [9]. Dort erfahren Webentwickler auch, welches passende Adapter-Plugin sie noch mit »npm« auf die Festplatte holen müssen.
Abgedeckt
Den Javascript-Code kann Karma noch vor dem eigentlichen Testlauf durch Präprozessoren schicken. Auf diesem Weg lässt sich beispielsweise die Testabdeckung über das Tool Istanbul [10] ermitteln. Ein passendes Plugin für Istanbul installiert zunächst der Befehl »npm install karma-coverage –save-dev«. Anschließend muss Karma noch wissen, dass es Istanbul starten soll.
Das erledigt in der Konfigurationsdatei die folgende Einstellung:
preprocessors: {
'js/*.js': 'coverage'
},
Damit die Ergebnisse von Istanbul nicht sinnlos im Nirwana verschwinden, muss ein Reporter sie noch aufbereiten:
reporters: ['progress', 'coverage'],
Weitere Anpassungen an der Konfigurationsdatei sind meist nicht nötig – mit einer Ausnahme: Der von Karma gezündete Webserver lauscht später standardmäßig an Port 9876. Sofern dieser bereits belegt ist, passt der Tester ihn neben »port« entsprechend an. Wer weitere Änderungen vornehmen muss oder möchte, findet auf der Karma-Website [11] eine ausführliche Referenz aller Variablen beziehungsweise Einstellungen.
Zündung!
Damit fehlen jetzt nur noch der zu testende Code sowie die Testfälle. Für die ersten Schritte mit Karma bietet sich die einfache Addition aus Listing 1 an: Die dortige Funktion addiert versehentlich immer nur dann die zwei Zahlen, wenn sie gleich sind. Einen dazu passenden Test für Jasmine zeigt Listing 2. Karma startet jetzt der Befehl:
karma start karma.conf.js
Der Name der Konfigurationsdatei darf entfallen, sofern sie »karma.conf.js«, »karma.conf.coffee« oder »karma.conf.ts« heißt. Karma startet im Anschluss den Webserver, dann die entsprechenden Browser und führt schließlich sämtliche Tests aus.
Listing 1
js/add.js
01 function add(x, y) {
02 if (x === y) {
03 return x + y;
04 }
05 }
Listing 2
tests/add.test.js
01 describe('Add-Funktion', function() {
02
03 it('addiert zwei ungerade zahlen', () => {
04 expect(add(2, 3)).toEqual(5);
05 });
06
07 });
Die Ergebnisse zeigt Karma dann in der Konsole an, wobei der Test in Abbildung 3 offensichtlich fehlschlägt. Ist auch Istanbul eingebunden, landen die Informationen über die Testabdeckung zudem im Projektverzeichnis im Unterordner »coverage« (Abbildung 4).

Abbildung 3: Karma startet hier den Webserver, dann Firefox. Der führt den Test aus Listing 2 aus.
Um den gemeldeten Fehler zu beheben, muss der Webentwickler in Listing 1 lediglich die Abfrage entfernen. Das laufende Karma erkennt die Änderungen an der entsprechenden Datei und führt den Test umgehend erneut aus.
Fazit
Karma automatisiert lediglich die Testausführung mit etablierten Test-Frameworks wie Jasmine. Entwickler dürfen ihre eigenen Frameworks weiterhin nutzen und müssen nicht umlernen.

Abbildung 4: Wie Istanbul im Coverage-Report zeigt, ist die Testabdeckung aus Listing 2 noch nicht optimal.
Das automatisierte Testen in mehreren Browsern gleichzeitig und unter realen Bedingungen beschleunigt den gesamten Testprozess drastisch. Karma ist zugleich praxiserprobt, stabil und durch die Plugins einfach zu erweitern. Wer komplexe Webanwendungen entwickeln möchte, sollte dem Test-Tool daher unbedingt eine Chance geben.
Nähere Informationen zur Funktionsweise von Karma finden sich in der Masterarbeit von Vojtêch Jína, die dem Karma-Quellcode beiliegt [12], aber auch auf der Heft-DVD präsent ist.
Infos
-
Jasmine: https://jasmine.github.io
-
Mocha: https://mochajs.org
-
Qunit: https://qunitjs.com
-
Jenkins: https://jenkins.io
-
Travis: https://travis-ci.org
-
Semaphore: https://semaphoreci.com
-
Liste aller Launcher: https://www.npmjs.org/browse/keyword/karma-launcher
-
Liste aller Adapter: https://npmjs.org/browse/keyword/karma-adapter
-
Istanbul: https://istanbul.js.org
-
Referenz der Konfigurationsdatei: https://karma-runner.github.io/3.0/config/configuration-file.html
-
Vojtêch Jína: “Javascript Test Runner” (Master-Arbeit): https://github.com/karma-runner/karma/raw/master/thesis.pdf






