Unit-Tests mit Node.js

Seite 3: Spezialitäten und Fazit

Inhaltsverzeichnis

Da Mocha sowohl die erwähnten Container als auch Tests mit ineinander verschachtelten Callbacks realisiert, lassen sich testübergreifende Aspekte leicht unter Verwendung der gängigen Sprachmittel von JavaScript implementieren. Alles, was beispielsweise innerhalb eines Containers als lokale Variable definiert ist, steht in allen darin enthaltenen Containern und Tests zur Verfügung.

Um solche Aspekte zu initialisieren beziehungsweise aufzulösen, kennt Mocha die beiden Funktionen setup und teardown. Sie werden vor beziehungsweise nach jedem einzelnen Test ausgeführt, der sich innerhalb des selben oder eines untergeordneten Containers befindet:

suite('fizzBuzz', function () {
var _fizzBuzz;
setup(function () {
_fizzBuzz = new FizzBuzz();
});
teardown(function () {
_fizzBuzz = undefined;
});
suite('isFizz', function () {
// ...
});
suite('isBuzz', function () {
// ...
});
});

Findet anstelle des TDD- der BDD-Stil Verwendung, tragen diese Funktionen die Namen beforeEach und afterEach. Im BDD-Stil kennt Mocha darüber hinaus noch die beiden Funktionen before und after, die pro Container nur ein einziges Mal ausgeführt werden. Funktionen mit äquivalenter Arbeitsweise für TDD stellt Mocha hingegen nicht zur Verfügung.

Auf den ersten Blick fällt es in Mocha ebenso leicht asynchronen Code zu testen, wie es bei synchronem Code der Fall ist. Wird beispielsweise statt

test('returns true for numbers divisible by 3', function () {
var actual = fizzBuzz.isFizz(3);
assert.that(actual, is.true());
});

der asynchrone Code

test('returns true for numbers divisible by 3', function () {
fizzBuzz.isFizz(3, function (actual) {
assert.that(actual, is.true());
});
});

verwendet, läuft der Test – eine korrekte Implementierung der isFizz-Funktion vorausgesetzt – ebenfalls erfolgreich durch und erhält eine entsprechende Markierung. Leider verhält sich das Prüfen asynchronen Codes tatsächlich nicht ganz so einfach, denn der erwähnte Test verläuft ganz unabhängig von der Korrektheit der Implementierung stets erfolgreich.

Der Grund hierfür liegt darin, dass Mocha nach dem Aufruf der isFizz-Funktion nicht weiß, dass noch ein Callback aussteht und entsprechend zu warten ist. Da der Tests an seinem scheinbaren Ende angelangt ist, ohne eine Ausnahme ausgelöst zu haben, wird er als erfolgreich markiert. Das Resultat der danach im Callback ausgeführten assert-Funktion kommt hierfür zu spät.

Als Lösung bietet Mocha einen ausgesprochen eleganten Weg an: Asynchrone Tests nehmen in ihrem Callback den sogenannten done-Parameter entgegen. Hinter ihm verbirgt sich ein weiterer Callback. Mocha wartet bei dessen Verwendung bis zu seinem Aufruf mit dem Abschluss des Tests. Schreibt man den zuvor fehlerhaften Test also zu

test('returns true for numbers divisible by 3', function (done) {
fizzBuzz.isFizz(3, function (actual) {
assert.that(actual, is.true());
done();
});
});

um, führt Mocha ihn korrekt aus und wartet wie gewünscht auf den Aufruf der beiden Callback-Funktionen.

Zu guter Letzt kann man Mocha dazu verwenden, clientseitigen Code zu testen und die Tests innerhalb des Webbrowsers auszuführen. Der Initialisierungscode hierfür ist ein wenig aufwendiger als unter Node.js. Da er in der Regel jedoch für jede Anwendung gleich aussieht, lässt er sich jederzeit aus einer einmalig erzeugten Vorlage kopieren, wie es auch bei der Datei mocha.opts der Fall ist.

Um Mocha im Webbrowser auszuführen, sind zunächst die beiden Dateien mocha.js und mocha.css herunterzuladen und in eine HTML-Seite einzubinden. Die beiden Dateien finden sich im GitHub-Repository von Mocha. Darüber hinaus ist ein Modul vonnöten, das die Assert-Phase übernimmt. Mit ihm ist ebenso zu verfahren.

Mocha erzeugt die Ausgabe bei Verwendung im Browser innerhalb eines div-Elements, das die ID mocha tragen muss. Das Grundgerüst der Webseite zur Ausführung clientseitiger Unit-Tests auf Basis von Mocha sieht daher wie folgt aus:

<!doctype html>
<html>
<head>
<title>Unit-Tests</title>
<link rel="stylesheet" type="text/css" href="mocha.css" />
<script type="text/javascript" src="mocha.js"></script>
<script type="text/javascript">
mocha.setup({
ui: 'tdd'
});
</script>
<script type="text/javascript" src="expect.js"></script>

<!-- Systems under test go here -->
<script type="text/javascript" src="foo.js"></script>
<script type="text/javascript" src="[...].js"></script>

<!-- Unit tests go here -->
<script type="text/javascript" src="fooTests.js"></script>
<script type="text/javascript" src="[...]Tests.js"></script>
</head>
<body>
<div id="mocha"></div>
<script type="text/javascript">
mocha.run();
</script>
</body>
</html>

Die eigentlichen Tests sind auf die gleiche Art zu schreiben wie die Tests für Node.js. Besonders interessant ist die Kombination von Mocha mit dem Datei- und Modullader Require.js, bei der sich tatsächlich in jeder Anwendung stets die selbe Datei index.html verwenden lässt, und die einzelnen Tests auf den jeweils zu testenden Code als Abhängigkeit verweisen.

Mocha ist ein einfach zu handhabendes, zugleich aber auch leistungsfähiges Testframework, das Unit-Tests nicht nur unter Node.js, sondern auch im Rahmen des Webbrowsers ermöglicht. Durch die Unterstützung verschiedener Teststile wie TDD und BDD und die durchdachten Möglichkeiten zur Strukturierung und Organisation von Tests stellt Mocha eins der besten Frameworks für Unit-Tests in
JavaScript dar. Nicht zuletzt die einfache Vorgehensweise zum Testen asynchronen Codes macht es besonders interessant. Da es dem Entwickler zudem an verschiedenen Stellen ein gutes Maß an Wahlfreiheit lässt, ist es ausreichend flexibel, um auch unterschiedliche Philosophien anstandslos bedienen zu können.

Golo Roden

ist Gründer und Geschäftsführer der "the native web UG", eines auf native Webtechniken spezialisierten Unternehmens. Für die Entwicklung moderner Webanwendungen bevorzugt er JavaScript und Node.js und hat mit "Node.js & Co." das erste deutschsprachige Buch zum Thema geschrieben.