Unit-Tests mit Node.js

Seite 2: Schreiben und strukturieren

Inhaltsverzeichnis

Einen Test zu schreiben, fällt in Mocha leicht: Pro Test ist die vorgefertigte Funktion test aufzurufen und ihr der Name des gewünschten Tests zu übergeben.

test('Numbers divisible by 3 return Fizz');

Das funktioniert allerdings nur, beim Aufruf von Mocha unter Angabe des Kommandozeilenparameter --ui tdd zum Aktivieren des TDD-Stils (Test-Driven-Development). Bei Nutzung des von Haus aus voreingestellten BDD-Stils (Behaviour-Driven-Development), lautet der Name der aufzurufenden Funktion stattdessen it:

it('should return Fizz for numbers divisble by 3');

Welcher Stil der Entwickler bevorzugt, ist zumindest in Mocha eine rein syntaktische Frage und hat keinerlei Auswirkungen auf verfügbare Funktionen oder Sonstiges. Da der Autor des Artikels den TDD-Stil favorisiert, werden alle folgenden Beispiele ohne weiteren Hinweis auf diese Art angegeben.

Beim Ausführen von Mocha wird der Test nun als inconclusive markiert, da er zwar nicht fehlschlägt, allerdings auch keinerlei Implementierung enthält. Diese übergibt man der test-Funktion als zweiten Parameter im Rahmen eines Callbacks:

test('Numbers divisible by 3 return Fizz', function () {
// ...
});

Die interne Logik von Mocha arbeitet verhältnismäßig einfach: Wird der Callback erfolgreich ausgeführt, also, ohne dass eine Ausnahme eintritt, wird der Test als bestanden angesehen – sonst als fehlgeschlagen.

Mocha lässt dem Entwickler bei all dem die Wahl, welches konkrete Modul er für die Durchführung der Assert-Phase verwenden möchte. Es ist also durchaus möglich, das eingangs erwähnte, in Node.js integrierte assert-Modul zu verwenden. Alternativ steht aber auch eine Vielzahl an geeigneten Modulen von Drittanbietern zur Verfügung, die nahezu jeden erdenklichen Stil abdecken. Ein kleiner Überblick findet sich im Exkurs unter "assert, should & Co.".

Tests, für deren Ausführung Mocha länger als 75 Millisekunden benötigt, werden als langlaufend angesehen und in den meisten Ausgabeformaten entsprechend markiert. Braucht Mocha hingegen für die Ausführung eines Tests länger als 2000 Millisekunden, bricht es ihn als fehlgeschlagen ab. Dieser Timeout lässt sich nicht nur global über den Kommandozeilenparameter --timeout, sondern auch individuell pro Test festlegen. Dazu muss man innerhalb eines Tests die Funktion timeout aus this aufrufen und ihr die Anzahl der Millisekunden übergeben, die zu warten ist, bevor der Test abzubrechen ist:

test('Numbers divisible by 3 return Fizz', function () {
this.timeout(5 * 1000);
// ...
});

Mocha bietet einfache, aber leistungsstarke Hilfsmittel an, um Tests übersichtlich zu strukturieren. Zum einen wird pro zu testender Datei eine gleichnamige Testdatei angelegt, wobei der Name zur besseren Lesbarkeit um das Suffix Tests ergänzt werden kann. Die der Anwendung zu Grunde liegende Verzeichnisstruktur klont Mocha dabei innerhalb des test-Verzeichnisses, sodass die folgende Struktur entsteht:

/
+- foo.js
+- bar
! +- baz.js
+- test
+- fooTests.js
+- mocha.opts
+- bar
+- bazTests.js

In diesem Szenario muss man lediglich bedenken, Mocha mit dem Parameter --recursive aufzurufen oder ihn der Datei mocha.opts hinzuzufügen, damit das Framework auch die Unterverzeichnisse des test-Verzeichnisses bei der Suche nach Testdateien berücksichtigt.

Zum anderen wird innerhalb einer Testdatei für jeden zu prüfenden Fall ein eigener Test angelegt. Deren Anzahl wächst unter Umständen allerdings enorm an. Um die Übersicht zu verbessern, wäre es wünschenswert, einzelne Tests nochmals in logische Container innerhalb einer Datei gruppieren zu können. Genau diesen Zweck erfüllt in Mocha die suite-Funktion, die ähnlich zu der test-Funktion agiert, allerdings keinen eigenständige Überprüfung, sondern ausschließlich einen Container dafür definiert:

suite('isFizz', function () {
test('returns true for numbers divisible by 3', function () {
// ...
});
test('returns false for numbers not divisible by 3', function () {
// ...
});
});

Ausgesprochen praktisch ist, dass Testcontainer sich ihrerseits wiederum auf beliebige Art verschachteln lassen:

suite('fizzBuzz', function () {
suite('isFizz', function () {
// ...
});
suite('isBuzz', function () {
// ...
});
});

Nutzt man statt des TDD- den BDD-Stil, verwendet Mocha als Namen für die Containerfunktion nicht suite, sondern describe. Abgesehen von diesem Unterschied in der Namensgebung verhalten sich beide Funktionen allerdings identisch.