Tutorial

So testen Sie ein Node.js-Modul mit Mocha und Assert

Published on May 1, 2020
Deutsch
So testen Sie ein Node.js-Modul mit Mocha und Assert

Die Autorin wählte den Open Internet/Free Speech Fund, um eine Spende im Rahmen des Programms Write for DOnations zu erhalten.

Einführung

Testen ist ein integraler Bestandteil der Softwareentwicklung. Es ist üblich, dass Programmierer Code ausführen, der ihre Anwendung testet, während sie darin Änderungen vornehmen. So können sie bestätigen, dass sich die Anwendung so verhält, wie sie es gerne hätten. Mit dem richtigen Test-Setup kann dieser Prozess sogar automatisiert sein und somit eine Menge Zeit sparen. Das Ausführen von Tests nach dem Schreiben von neuem Code stellt sicher, dass neue Änderungen keine bereits vorhandenen Funktionen brechen. Das gibt dem Entwickler Vertrauen in seine Code-Basis, insbesondere dann, wenn der Code produktiv eingesetzt wird, damit die Benutzer mit ihm interagieren können.

Ein Test-Framework strukturiert die Art, wie wir Testfälle erstellen. Mocha ist ein beliebtes JavaScript-Framework, das unsere Testfälle organisiert und für uns ausführt. Mocha verifiziert jedoch nicht das Verhalten unseres Codes. Um Werte in einem Test zu vergleichen, können wir das Node.js-assert-Modul verwenden.

In diesem Artikel schreiben Sie Tests für ein Node.js-TODO-Listenmodul. Sie richten das Testframework von Mocha ein und nutzen es, um Ihre Tests zu strukturieren. Dann verwenden Sie das Node.js-assert-Modul, um die Tests selbst zu erstellen. In diesem Sinne verwenden Sie Mocha als Planersteller und assert zur Umsetzung des Plans.

Voraussetzungen

Schritt 1 — Schreiben eines Node-Moduls

Beginnen wir diesen Artikel mit dem Schreiben des Node.js-Moduls, das wir testen möchten. Dieses Modul verwaltet eine Liste von TODO-Elementen. Mithilfe dieses Moduls können wir alle TODOs auflisten, die wir verfolgen, sowie neue Elemente hinzufügen und einige als abgeschlossen markieren. Zusätzlich können wir eine Liste von TODO-Elementen in eine CSV-Datei exportieren. Wenn Sie eine Auffrischung über das Schreiben von Node.js-Modulen wünschen, können Sie unseren Artikel Erstellen eines Node.js-Moduls lesen.

Zuerst müssen wir die Codierungsumgebung einrichten. Erstellen Sie einen Ordner mit dem Namen Ihres Projekts in Ihrem Terminal. Dieses Tutorial verwendet den Namen todos:

  1. mkdir todos

Gehen Sie dann in diesen Ordner hinein:

  1. cd todos

Initialisieren Sie nun npm, da wir später zum Ausführen der Tests seine CLI-Funktionalität verwenden:

  1. npm init -y

Wir haben nur eine Abhängigkeit, Mocha, das wir zur Organisation und Durchführung unserer Tests verwenden. Zum Herunterladen und Installieren von Mocha verwenden Sie Folgendes:

  1. npm i request --save-dev mocha

Wir installieren Mocha als dev-Abhängigkeit, da es für das Modul in einer Produktionsumgebung nicht erforderlich ist. Wenn Sie mehr über Node.js-Packages oder npm erfahren möchten, lesen Sie den Leitfaden Verwenden von Node.js-Modulen mit npm und package.json.

Abschließend erstellen wir unsere Datei, die den Code unseres Moduls enthalten wird:

  1. touch index.js

Damit können wir nun unser Modul erstellen. Öffnen Sie index.js in einem Texteditor wie nano:

  1. nano index.js

Zuerst definieren wir die Todos-Klasse. Diese Klasse enthält alle Funktionen, die wir zur Verwaltung unserer TODO-Liste benötigen. Fügen Sie index.js die folgenden Zeilen von Code hinzu:

todos/index.js
class Todos {
    constructor() {
        this.todos = [];
    }
}

module.exports = Todos;

Wir beginnen mit der Datei, indem wir eine Todos-Klasse erstellen. Seine Funktion constructor() nimmt keine Argumente an, daher müssen wir keine Werte bereitstellen, um ein Objekt für diese Klasse zu instanziieren. Wenn wir ein Todos-Objekt initialisieren, erstellen wir lediglich eine todos-Funktion, bei der es sich um ein leeres Array handelt.

Die modules-Zeile ermöglicht es anderen Node.js-Modulen, unsere Todos-Klasse zu verlangen. Wenn wir sie nicht ausdrücklich exportieren, könnte die Testdatei, die wir später erstellen, sie nicht verwenden.

Wir fügen nun eine Funktion hinzu, um das Array von todos, das wir gespeichert haben, auszugeben. Fügen Sie die folgenden hervorgehobenen Zeilen ein:

todos/index.js
class Todos {
    constructor() {
        this.todos = [];
    }

    list() {
        return [...this.todos];
    }
}

module.exports = Todos;

Unsere list()-Funktion gibt eine Kopie des Arrays aus, die von der Klasse verwendet wird. Sie erstellt die Kopie des Arrays mit der destrukturierten Syntax von JavaScript. Wir erstellen eine Kopie des Arrays, damit die Änderungen, die der Benutzer an dem von list() ausgegebenen Array vornimmt, nicht das vom Todos-Objekt verwendete Array beeinträchtigen.

Anmerkung: JavaScript-Arrays sind Referenztypen. Das bedeutet, dass sich JavaScript bei jeder Variablenzuweisung an ein Array oder bei Funktionsaufrufen mit einem Array als Parameter auf das ursprüngliche Array bezieht, das erstellt wurde. Wenn wir zum Beispiel ein Array mit drei Elementen namens x haben und eine neue Variable y erstellen, sodass y = x, y und x sich beide auf dieselbe Sache beziehen. Alle Änderungen, die wir im Array an y vornehmen, wirken sich auf die Variable x aus und umgekehrt.

Schreiben wir nun die add()-Funktion, die ein neues TODO-Element hinzufügt:

todos/index.js
class Todos {
    constructor() {
        this.todos = [];
    }

    list() {
        return [...this.todos];
    }

    add(title) {
        let todo = {
            title: title,
            completed: false,
        }

        this.todos.push(todo);
    }
}

module.exports = Todos;

Unsere add()-Funktion nimmt eine Zeichenfolge und platziert sie bei einem neuen JavaScript-Object in die title-Funktion. Das neue Objekt hat auch eine completed-Funktion, die standardmäßig auf false gesetzt ist. Dann fügen wir unser neues Objekt unserem Array von TODOs hinzu.

Eine wichtige Funktionalität in einem TODO-Manager ist die Markierung von Elementen als abgeschlossen. Um dies umzusetzen, durchlaufen wir unser todos-Array, um das TODO-Element zu finden, nach dem der Benutzer sucht. Wenn eines gefunden wird, markieren wir es als abgeschlossen. Wenn keines gefunden wird, geben wir einen Fehler aus.

Fügen Sie die complete()-Funktion wie hier gezeigt hinzu:

todos/index.js
class Todos {
    constructor() {
        this.todos = [];
    }

    list() {
        return [...this.todos];
    }

    add(title) {
        let todo = {
            title: title,
            completed: false,
        }

        this.todos.push(todo);
    }

    complete(title) {
        let todoFound = false;
        this.todos.forEach((todo) => {
            if (todo.title === title) {
                todo.completed = true;
                todoFound = true;
                return;
            }
        });

        if (!todoFound) {
            throw new Error(`No TODO was found with the title: "${title}"`);
        }
    }
}

module.exports = Todos;

Speichern Sie die Datei und beenden Sie den Texteditor.

Wir verfügen nun über einen grundlegenden TODO-Manager, mit dem wir experimentieren können. Als Nächstes testen wir den Code manuell, um zu sehen, ob die Anwendung funktioniert.

Schritt 2 — Manuelles Testen des Codes

In diesem Schritt führen wir die Funktionen unseres Codes aus und beobachten die Ausgabe, um sicherzustellen, dass sie unseren Erwartungen entspricht. Das nennt sich manuelles Testen. Es ist wahrscheinlich die gebräuchlichste Testmethodik, die Programmierer anwenden. Obwohl wir unsere Tests später mit Mocha automatisieren, testen wir unseren Code zunächst manuell, um ein besseres Gefühl dafür zu bekommen, wie sich manuelles Testen von Test-Frameworks unterscheidet.

Wir fügen unserer Anwendung zwei TODO-Elemente hinzu und markieren eines als abgeschlossen. Starten Sie die Node.js REPL in dem gleichen Ordner wie die Datei index.js:

  1. node

Sie sehen die Eingabeaufforderung > in der REPL, die uns anzeigt, dass wir JavaScript-Code eingeben können. Geben Sie Folgendes an der Eingabeaufforderung ein:

  1. const Todos = require('./index');

Mit require() laden wir das TODOs-Modul in eine Todos-Variable. Erinnern Sie sich, dass unser Modul die Todos-Klasse standardmäßig ausgibt.

Lassen Sie uns nun ein Objekt für diese Klasse instanziieren. Fügen Sie diese Zeile von Code in der REPL hinzu:

  1. const todos = new Todos();

Wir können das todos-Objekt verwenden, um unsere Umsetzungsarbeiten zu verifizieren. Fügen wir unser erstes TODO-Element hinzu:

  1. todos.add("run code");

Bisher haben wir noch keine Ausgabe in unserem Terminal gesehen. Verifizieren wir, dass wir unser "run code"-TODO-Element gespeichert haben, indem wir eine Liste aller unserer TODOs abrufen:

  1. todos.list();

Sie sehen diese Ausgabe in Ihrer REPL:

Output
[ { title: 'run code', completed: false } ]

Das ist das erwartete Ergebnis: Wir haben ein TODO-Element in unserem Array von TODOs und es ist nicht standardmäßig abgeschlossen.

Wir fügen ein weiteres TODO-Element hinzu:

  1. todos.add("test everything");

Markieren Sie das erste TODO-Element als abgeschlossen:

  1. todos.complete("run code");

Unser todos-Objekt verwaltet nun zwei Elemente: "run code" und "test everything". Das "run code"-TODO wird ebenfalls abgeschlossen sein. Bestätigen wir dies, indem wir list() erneut aufrufen:

  1. todos.list();

Die REPL wird Folgendes ausgeben:

Output
[ { title: 'run code', completed: true }, { title: 'test everything', completed: false } ]

Beenden Sie nun die REPL wie folgt:

  1. .exit

Wir haben bestätigt, dass sich unser Modul erwartungsgemäß verhält. Wir haben unseren Code nicht in eine Testdatei gestellt oder eine Testbibliothek verwendet, sondern manuell getestet. Leider ist diese Testform zeitaufwendig, wenn wir sie bei jeder Änderung, die wir vornehmen, durchführen. Als Nächstes verwenden wir automatisiertes Testen in Node.js und sehen, ob wir dieses Problem mit dem Mocha lösen können.

Schritt 3 — Schreiben des ersten Tests mit Mocha und Assert

Im letzten Schritt haben wir unsere Anwendung manuell getestet. Das funktioniert für individuelle Anwendungsfälle, aber wenn unser Modul skaliert, wird diese Methode weniger praktikabel. Wenn wir neue Eigenschaften testen, müssen wir sicher sein, dass die hinzugefügte Funktionalität keine Probleme in der alten Funktionalität verursacht. Wir möchten jede Eigenschaft bei jeder Änderung des Codes erneut testen, aber dies von Hand zu tun wäre sehr aufwendig und fehleranfällig.

Eine effizientere Praxis wäre die Einrichtung automatisierter Tests. Das sind skriptbasierte Tests, die wie jeder andere Codeblock geschrieben sind. Wir führen unsere Funktionen mit definierten Eingaben aus und inspizieren ihre Effekte, um sicherzustellen, dass sie sich wie erwartet verhalten. Mit dem Anwachsen unserer Codebasis wächst auch der Umfang der automatisierten Tests. Wenn wir neue Tests zusammen mit den Eigenschaften schreiben, können wir überprüfen, ob das gesamte Modul noch funktioniert – ohne sich jedesmal daran erinnern zu müssen, wie jede einzelne Funktion genutzt wird.

In diesem Tutorial verwenden wir das Testframework von Mocha mit dem Node.js-assert-Modul. Wir sammeln ein paar praktische Erfahrungen, um zu sehen, wie sie zusammenarbeiten.

Erstellen Sie zunächst eine neue Datei, um unseren Testcode zu speichern:

  1. touch index.test.js

Verwenden Sie nun Ihren bevorzugten Texteditor, um die Testdatei zu öffnen. Sie können wie zuvor nano verwenden:

  1. nano index.test.js

In der ersten Zeile der Textdatei laden wir das TODOs-Modul, so wie wir es mit der Node.js-Shell getan haben. Dann laden wir das assert-Modul für das Schreiben unserer Tests. Fügen Sie die folgenden Zeilen hinzu:

todos/index.test.js
const Todos = require('./index');
const assert = require('assert').strict;

Die strict-Funktion des assert-Moduls erlaubt uns, spezielle Gleichheitstests anzuwenden, die von Node.js empfohlen werden und gut für das zukünftige Prüfen geeignet sind, da sie mehr Anwendungsfälle berücksichtigen.

Bevor wir mit dem Schreiben von Tests beginnen, behandeln wir, wie Mocha unseren Code organisiert. Die in Mocha strukturierten Tests folgen normalerweise dieser Vorlage:

describe([String with Test Group Name], function() {
    it([String with Test Name], function() {
        [Test Code]
    });
});

Beachten Sie zwei Schlüsselfunktionen: describe() und it(). Die describe()-Funktion wird zur Gruppierung ähnlicher Tests genutzt. Es ist nicht erforderlich, für Mocha Tests auszuführen, aber die Gruppierung von Tests erleichtert die Pflege unseres Testcodes. Es wird empfohlen, Ihre Tests so zu gruppieren, dass Sie ähnliche Tests leicht zusammen aktualisieren können.

Die Funktion it() enthält unseren Testcode. Hier würden wir mit den Funktionen unseres Moduls interagieren und die assert-Bibliothek verwenden. Viele it()-Funktionen können als describe()-Funktion definiert werden.

Unser Ziel in diesem Abschnitt ist die Verwendung von Mocha und assert zur Automatisierung unserer manuellen Tests. Wir führen dies Schritt für Schritt aus und beginnen mit unserem Beschreibungsblock. Fügen Sie Folgendes nach den Modulzeilen in Ihre Datei ein:

todos/index.test.js
...
describe("integration test", function() {
});

Mit diesem Codeblock haben wir eine Gruppierung für unsere integrierten Tests erstellt. Komponententests würden jeweils nur eine Funktion testen. Integrationstests verifizieren, wie gut Funktionen innerhalb oder über Module zusammenarbeiten. Wenn Mocha unseren Test ausführt, laufen alle Tests innerhalb des Beschreibungsblocks unter der "integration test"-Gruppe.

Wir fügen nun eine it()-Funktion hinzu, damit wir mit dem Testen des Codes unseres Moduls beginnen können:

todos/index.test.js
...
describe("integration test", function() {
    it("should be able to add and complete TODOs", function() {
    });
});

Beachten Sie, wie deskriptiv wir den Namen des Tests gemacht haben. Wenn jemand unseren Test ausführt, wird sofort klar, was passiert oder fehlgeschlagen ist. Eine gut getestete Anwendung ist typischerweise eine gut dokumentierte Anwendung und Tests können manchmal eine effektive Art der Dokumentation sein.

Für unseren ersten Test erstellen wir ein neues Todos-Objekt und verifizieren, dass es keine Elemente enthält:

todos/index.test.js
...
describe("integration test", function() {
    it("should be able to add and complete TODOs", function() {
        let todos = new Todos();
        assert.notStrictEqual(todos.list().length, 1);
    });
});

Die erste neue Zeile des Codes instanziierte ein neues Todos-Objekt, wie wir es in der Node.js-REPL oder einem anderen Modul tun würden. In der zweiten neuen Zeile verwenden wir das assert-Modul.

Aus dem assert-Modul verwenden wir die notStrictEqual()-Methode. Diese Funktion nimmt zwei Parameter: den Wert, den wir testen möchten (genannt actual-Wert) und den Wert, den wir erhalten möchten (genannt expected-Wert). Wenn beide Argumente gleich sind, gibt notStrictEqual() einen Fehler aus, damit der Test fehlschlägt.

Speichern und beenden Sie index.test.js.

Der Basisfall wird wahr sein, da die Länge 0 sein sollte, was nicht 1 ist. Bestätigen wir das, indem wir Mocha ausführen. Dazu müssen wir unsere package.json-Datei ändern. Öffnen Sie Ihre package.json-Datein mit Ihrem Texteditor:

  1. nano package.json

Ändern Sie diese nun in der scripts-Funktion so wie hier gezeigt:

todos/package.json
...
"scripts": {
    "test": "mocha index.test.js"
},
...

Wir haben nun das Verhalten des npm-CLI-Befehls test geändert. Wenn wir npm test ausführen, überprüft npm den gerade eingegebenen Befehl in package.json. Die Ausführung sucht nach der Mocha-Bibliothek in unserem node_modules-Ordner und führt den mocha-Befehl mit unserer Testdatei aus.

Speichern und beenden Sie package.json.

Nun sehen wir uns an, was passiert, wenn wir unseren Test ausführen. Geben Sie Folgendes in Ihr Terminal ein:

  1. npm test

Der Befehl erzeugt die folgende Ausgabe:

Output
> todos@1.0.0 test your_file_path/todos > mocha index.test.js integrated test ✓ should be able to add and complete TODOs 1 passing (16ms)

Diese Ausgabe zeigt uns zunächst, welche Testgruppe sie nun ausführen wird. Für jeden einzelnen Test innerhalb einer Gruppe ist der Testfall einbezogen. Wir sehen unseren Testnamen wie in der Funktion it() beschrieben. Das Häkchen auf der linken Seite des Testfalls zeigt an, dass der Test bestanden ist.

Am Ende erhalten wir eine Zusammenfassung aller Tests. In unserem Fall ist unser einzelner Test bestanden und wurde in 16 ms abgeschlossen (die Zeit variiert von Computer zu Computer).

Unsere Testung hat erfolgreich begonnen. Der aktuelle Testfall kann jedoch falsch-positive Meldungen liefern. Ein falsch-positiver Testfall ist ein Testfall, der bestanden wird, wenn er fehlschlagen sollte.

Wir überprüfen gerade, dass die Länge des Arrays nicht gleich 1 ist. Wir werden den Test nun so ändern, dass dieser Zustand auch dann zutrifft, wenn er es nicht sollte. Fügen Sie der index.test.js folgende Zeilen hinzu:

todos/index.test.js
...
describe("integration test", function() {
    it("should be able to add and complete TODOs", function() {
        let todos = new Todos();
        todos.add("get up from bed");
        todos.add("make up bed");
        assert.notStrictEqual(todos.list().length, 1);
    });
});

Speichern und schließen Sie die Datei.

Wir haben zwei TODO-Elemente hinzugefügt. Wir führen nun den Test aus, um zu sehen, was passiert:

  1. npm test

Dadurch ergibt sich Folgendes:

Output
... integrated test ✓ should be able to add and complete TODOs 1 passing (8ms)

Wie ewartet besteht der Test, da die Länge größer als 1 ist. Jedoch wird der ursprüngliche Zweck des ersten Tests verfehlt. Der erste Test ist dazu gedacht, zu bestätigen, dass wir mit einem Leerzustand beginnen. Ein besserer Test bestätigt dies in allen Fällen.

Wir ändern nun den Test, damit er nur dann bestanden wird, wenn wir absolut keine TODOs im Speicher haben. Führen Sie die folgenden Änderungen in der index.test.js aus:

todos/index.test.js
...
describe("integration test", function() {
    it("should be able to add and complete TODOs", function() {
        let todos = new Todos();
        todos.add("get up from bed");
        todos.add("make up bed");
        assert.strictEqual(todos.list().length, 0);
    });
});

Sie haben notStrictEqual() auf strictEqual() geändert, eine Funktion, die die Gleichheit zwischen dem tatsächlichen und erwarteten Argument überprüft. Die Strict-Equal-Funktion schlägt fehl, wenn unsere Argumente nicht genau gleich sind.

Speichern und beenden Sie und führen Sie dann den Test aus, damit wir sehen können, was passiert:

  1. npm test

Dieses Mal zeigt die Ausgabe einen Fehler:

Output
... integration test 1) should be able to add and complete TODOs 0 passing (16ms) 1 failing 1) integration test should be able to add and complete TODOs: AssertionError [ERR_ASSERTION]: Input A expected to strictly equal input B: + expected - actual - 2 + 0 + expected - actual -2 +0 at Context.<anonymous> (index.test.js:9:10) npm ERR! Test failed. See above for more details.

Dieser Text wird uns helfen, herauszufinden, warum der Test fehlgeschlagen ist. Beachten Sie, dass zu Beginn des Testfalls kein Häkchen vorhanden ist, da der Test fehlgeschlagen ist.

Unsere Testzusammenfassung befindet sich nicht mehr am Ende der Ausgabe, sondern direkt nach der Anzeige unserer Liste von Testfällen:

...
0 passing (29ms)
  1 failing
...

Die verbleibende Ausgabe gibt uns Daten über unsere fehlgeschlagenen Tests. Zuerst sehen wir, welcher Testfall fehlgeschlagen ist:

...
1) integrated test
       should be able to add and complete TODOs:
...

Dann sehen wir, warum unser Test fehlgeschlagen ist:

...
      AssertionError [ERR_ASSERTION]: Input A expected to strictly equal input B:
+ expected - actual

- 2
+ 0
      + expected - actual

      -2
      +0

      at Context.<anonymous> (index.test.js:9:10)
...

Es wird ein AssertionError gemeldet, wenn strictEqual() fehlschlägt. Wir sehen, dass der expected-Wert, 0, vom actual-Wert, 2, abweicht.

Dann sehen wir die Zeile in unserer Testdatei, in der der Code fehlschlägt. In diesem Fall ist es Zeile 10.

Nun haben wir selbst gesehen, dass unser Test fehlschlägt, wenn wir fehlerhafte Werte erwarten. Wir ändern unseren Testfall wieder auf seinen richtigen Wert. Öffnen Sie die Datei:

  1. nano index.test.js

Dann nehmen Sie die todos.add-Zeilen heraus, sodass Ihr Code wie folgt aussieht:

todos/index.test.js
...
describe("integration test", function () {
    it("should be able to add and complete TODOs", function () {
        let todos = new Todos();
        assert.strictEqual(todos.list().length, 0);
    });
});

Speichern und schließen Sie die Datei.

Führen Sie ihn erneut aus, um zu bestätigen, dass er ohne potenzielle falsch-positive Meldungen besteht:

  1. npm test

Sie erhalten folgende Ausgabe:

Output
... integration test ✓ should be able to add and complete TODOs 1 passing (15ms)

Wir haben nun die Belastbarkeit unseres Tests deutlich verbessert. Fahren wir mit unserem Integrationstest fort. Der nächste Schritt ist das Hinzufügen eines neuen TODO-Elements in index.test.js:

todos/index.test.js
...
describe("integration test", function() {
    it("should be able to add and complete TODOs", function() {
        let todos = new Todos();
        assert.strictEqual(todos.list().length, 0);

        todos.add("run code");
        assert.strictEqual(todos.list().length, 1);
        assert.deepStrictEqual(todos.list(), [{title: "run code", completed: false}]);
    });
});

Nach der Verwendung der add()-Funktion bestätigen wir, dass wir nun ein TODO haben, das von unserem todos-Objekt mit strictEqual() verwaltet wird. Unser nächster Test bestätigt die Daten in den todos mit deepStrictEqual(). Die Funktion deepStrictEqual() prüft rekursiv, ob unsere erwarteten und tatsächlichen Objekte die gleichen Eigenschaften haben. In diesem Fall testet sie, dass die von uns erwarteten Arrays beide ein JavaScript-Objekt beinhalten. Dann überprüft sie, dass ihre JavaScript-Objekte die gleichen Eigenschaften haben, d. h., ihre beiden title-Eigenschaften "run code" und ihre beiden completed-Eigenschaften false sind.

Dann schließen wir die restlichen Tests unter Verwendung dieser beiden Gleichheitsprüfungen nach Bedarf durch Hinzufügen der folgenden hervorgehobenen Zeilen ab:

todos/index.test.js
...
describe("integration test", function() {
    it("should be able to add and complete TODOs", function() {
        let todos = new Todos();
        assert.strictEqual(todos.list().length, 0);

        todos.add("run code");
        assert.strictEqual(todos.list().length, 1);
        assert.deepStrictEqual(todos.list(), [{title: "run code", completed: false}]);

        todos.add("test everything");
        assert.strictEqual(todos.list().length, 2);
        assert.deepStrictEqual(todos.list(),
            [
                { title: "run code", completed: false },
                { title: "test everything", completed: false }
            ]
        );

        todos.complete("run code");
        assert.deepStrictEqual(todos.list(),
            [
                { title: "run code", completed: true },
                { title: "test everything", completed: false }
            ]
    );
  });
});

Speichern und schließen Sie die Datei.

Unser Test imitiert nun unseren manuellen Test. Mit diesen programmatischen Tests müssen wir die Ausgabe nicht kontinuierlich überprüfen, um zu sehen, ob unsere Tests bei der Ausführung bestehen. Üblicherweise möchte man jeden Aspekt der Verwendung testen, um sicherzustellen, dass der Code ordnungsgemäß getestet wird.

Wir führen unseren Test erneut mit npm test aus, um diese bekannte Ausgabe zu erhalten:

Output
... integrated test ✓ should be able to add and complete TODOs 1 passing (9ms)

Sie haben nun einen integrierten Test mit dem Mocha-Framework und der assert-Bibliothek eingerichtet.

Gehen wir nun von einer Situation aus, in der wir unser Modul mit einigen anderen Entwicklern geteilt haben und diese uns jetzt Feedback geben. Viele unserer Benutzer würden sich wünschen, dass die Funktion complete() einen Fehler meldet, wenn bisher noch keine TODOs hinzugefügt wurden. Wir fügen diese Funktionalität in unserer Funktion complete() ein.

Öffnen Sie index.js in Ihrem Texteditor:

  1. nano index.js

Fügen Sie der Funktion Folgendes hinzu:

todos/index.js
...
complete(title) {
    if (this.todos.length === 0) {
        throw new Error("You have no TODOs stored. Why don't you add one first?");
    }

    let todoFound = false
    this.todos.forEach((todo) => {
        if (todo.title === title) {
            todo.completed = true;
            todoFound = true;
            return;
        }
    });

    if (!todoFound) {
        throw new Error(`No TODO was found with the title: "${title}"`);
    }
}
...

Speichern und schließen Sie die Datei.

Nun fügen wir einen neuen Test für diese neue Eigenschaft hinzu. Wir wollen verifizieren, ob ein Todos-Objekt, das keine Elemente enthält, unseren speziellen Fehler ausgibt, wenn wir es mit complete aufrufen.

Gehen Sie in die index.test.js zurück:

  1. nano index.test.js

Fügen Sie am Ende der Datei den folgenden Code hinzu:

todos/index.test.js
...
describe("complete()", function() {
    it("should fail if there are no TODOs", function() {
        let todos = new Todos();
        const expectedError = new Error("You have no TODOs stored. Why don't you add one first?");

        assert.throws(() => {
            todos.complete("doesn't exist");
        }, expectedError);
    });
});

Wie zuvor verwenden wir describe() und it(). Wir beginnen unseren Test mit der Erstellung eines neuen todos-Objekts. Dann definieren wir den Fehler, dessen Meldung wir erwarten, wenn wir die Funktion complete() aufrufen.

Als Nächstes verwenden wir die Funktion throws() des assert-Moduls. Diese Funktion wurde erstellt, damit wir die Fehler, die unser Code ausgibt, verifizieren können. Sein erstes Argument ist eine Funktion, die den Code enthält, der den Fehler ausgibt. Das zweite Argument ist der Fehler, dessen Meldung wir erwarten.

Führen Sie in Ihrem Terminal erneut die Tests mit npm test aus und Sie sehen die folgende Ausgabe:

Output
... integrated test ✓ should be able to add and complete TODOs complete() ✓ should fail if there are no TODOs 2 passing (25ms)

Diese Ausgabe zeigt den Nutzen, warum wir automatisiertes Testen mit Mocha und assert durchführen. Da unsere Tests schriftlich ausgearbeitet sind, verifizieren wir bei jeder Ausführung von npm test, dass alle unsere Tests bestehen. Wir mussten nicht manuell überprüfen, ob der andere Code noch funktioniert – wir wissen, dass es so ist, da der Test, den wir haben, bestand.

Bisher haben unsere Tests die Ergebnisse von synchronem Code verifiziert. Wir behandeln nun, wie wir unsere neu gewonnenen Testgewohnheiten anpassen müssten, um mit asynchronem Code arbeiten zu können.

Schritt 4 – Testen von asynchronem Code

Eine der Eigenschaften, die wir in unserem TODO-Modul benötigen, ist eine CSV-Exportfunktion. Damit werden alle gespeicherten TODOs zusammen mit dem abgeschlossenen Status in einer Datei ausgegeben. Das erfordert die Verwendung des Moduls fs – eines integrierten Node.js-Moduls für die Arbeit mit dem Dateisystem.

Das Schreiben in eine Datei ist eine asynchrone Operation. Es gibt viele Möglichkeiten, in eine Datei in Node.js zu schreiben. Wir können Callbacks, Promises oder die Schlüsselworte async/await verwenden. In diesem Abschnitt behandeln wir, wie wir Tests für diese verschiedenen Methoden schreiben.

Callbacks

Eine callback-Funktion ist eine Funktion, die als Argument in einer asynchronen Funktion verwendet wird. Sie wird aufgerufen, wenn die asynchrone Operation abgeschlossen ist.

Wir fügen unserer Todos-Klasse eine Funktion namens saveToFile() hinzu. Diese Funktion erstellt eine Zeichenfolge, indem Sie alle unsere TODO-Elemente durchläuft, und schreibt diese Zeichenfolge in eine Datei.

Öffnen Sie Ihre index.js-Datei:

  1. nano index.js

Fügen Sie den folgenden hervorgehobenen Code in die Datei ein:

todos/index.js
const fs = require('fs');

class Todos {
    constructor() {
        this.todos = [];
    }

    list() {
        return [...this.todos];
    }

    add(title) {
        let todo = {
            title: title,
            completed: false,
        }
        this.todos.push(todo);
    }

    complete(title) {
        if (this.todos.length === 0) {
            throw new Error("You have no TODOs stored. Why don't you add one first?");
        }

        let todoFound = false
        this.todos.forEach((todo) => {
            if (todo.title === title) {
                todo.completed = true;
                todoFound = true;
                return;
            }
        });

        if (!todoFound) {
            throw new Error(`No TODO was found with the title: "${title}"`);
        }
    }

    saveToFile(callback) {
        let fileContents = 'Title,Completed\n';
        this.todos.forEach((todo) => {
            fileContents += `${todo.title},${todo.completed}\n`
        });

        fs.writeFile('todos.csv', fileContents, callback);
    }
}

module.exports = Todos;

Zunächst müssen wir das fs-Modul in unsere Datei importieren. Dann haben wir unsere neue Funktion saveToFile() hinzugefügt. Unsere Funktion übernimmt eine Callback-Funktion, die genutzt wird, sobald die Schreiboperation der Datei abgeschlossen ist. In dieser Funktion erstellen wir eine fileContents-Variable, die die gesamte zu speichernde Zeichenfolge als Datei speichert. Sie wird mit den CSV-Titeln initialisiert. Dann durchlaufen wir jedes TODO-Element mit der forEach()-Methode des internen Arrays. Beim Durchlaufen fügen wir die title- und completed-Eigenschaften der einzelnen todos hinzu.

Zum Schluss verwenden wir das fs-Modul zum Schreiben der Datei mit der writeFile()-Funktion. Unser erstes Argument ist der Dateiname: todos.csv. Das zweite ist der Inhalt der Datei, in diesem Fall unsere fileContents-Variable. Das letzte Argument ist unsere Callback-Funktion, die alle Schreibfehler der Datei behandelt.

Speichern und schließen Sie die Datei.

Wir schreiben nun einen Test für unsere Funktion saveToFile. Unser Test führt zwei Dinge aus: Er überprüft die Existenz der Datei und verifiziert, dass sie den richtigen Inhalt hat.

Öffnen Sie die Datei index.test.js:

  1. nano index.test.js

Beginnen wir damit, das fs-Modul am Anfang der Datei zu laden, da wir es zum Testen unserer Ergebnisse verwenden werden:

todos/index.test.js
const Todos = require('./index');
const assert = require('assert').strict;
const fs = require('fs');
...

Am Ende der Datei fügen wir unseren neuen Testfall hinzu:

todos/index.test.js
...
describe("saveToFile()", function() {
    it("should save a single TODO", function(done) {
        let todos = new Todos();
        todos.add("save a CSV");
        todos.saveToFile((err) => {
            assert.strictEqual(fs.existsSync('todos.csv'), true);
            let expectedFileContents = "Title,Completed\nsave a CSV,false\n";
            let content = fs.readFileSync("todos.csv").toString();
            assert.strictEqual(content, expectedFileContents);
            done(err);
        });
    });
});

Wie zuvor verwenden wir describe(), um unseren Test getrennt von den anderen zu gruppieren, da er eine neue Funktionalität enthält. Die it()-Funktion unterscheidet sich leicht von unseren anderen. Normalerweise hat die von uns verwendete Callback-Funktion keine Argumente. Dieses Mal haben wir done als Argument. Wir benötigen dieses Argument, wenn wir Funktionen mit Callbacks testen. Die Callback-Funktion done() wird von Mocha verwendet, um ihr anzugeben, wenn eine asynchrone Funktion abgeschlossen ist.

Alle in Mocha getesteten Callback-Funktionen müssen den Callback done() aufrufen. Wäre dies nicht der Fall, würde Mocha nie wissen, ob die Funktion abgeschlossen ist und würde festgefahren auf ein Signal warten.

Wir erstellen nun unsere Todos-Instanz und fügen ihr ein einzelnes Element hinzu. Wir rufen die Funktion saveToFile() mit einem Callback auf, der einen Dateischreibfehler findet. Beachten Sie, wie unser Test für diese Funktion im Callback enthalten ist. Wenn unser Testcode außerhalb des Callbacks wäre, würde er fehlschlagen, solange der Code aufgerufen würde, bevor das Schreiben der Datei abgeschlossen wäre.

In unserer Callback-Funktion überprüfen wir zunächst, dass unsere Datei existiert:

todos/index.test.js
...
assert.strictEqual(fs.existsSync('todos.csv'), true);
...

Die Funktion fs.existsSync() gibt true aus, wenn der Dateipfad in ihrem Argument existiert, und andernfalls false.

Anmerkung: Die Funktionen des fs-Moduls sind standardmäßig asynchron. Sie bildeten jedoch für Schlüsselfunktionen synchrone Gegenstücke. Dieser Test ist einfacher, wenn synchrone Funktionen verwendet werden, da wir den asynchronen Code nicht schachteln müssen, um sicherzustellen, dass er funktioniert. Im fs-Modul enden synchrone Funktionen normalerweise mit "Sync" am Ende ihrer Namen.

Dann erstellen wir eine Variable, um unseren erwarteten Wert zu speichern:

todos/index.test.js
...
let expectedFileContents = "Title,Completed\nsave a CSV,false\n";
...

Wir verwenden readFileSync() des fs-Moduls zum synchronen Lesen der Datei:

todos/index.test.js
...
let content = fs.readFileSync("todos.csv").toString();
...

Wir geben readFileSync() den richtigen Pfad für die Datei: todos.csv. Da readFileSync() ein Buffer-Objekt ausgibt, das Binärdaten speichert, verwenden wir seine toString()-Methode, damit wir seinen Wert mit der Zeichenfolge vergleichen können, die wir voraussichtlich gespeichert haben.

Wie zuvor verwenden wir das strictEqual des assert-Moduls, um einen Vergleich durchzuführen:

todos/index.test.js
...
assert.strictEqual(content, expectedFileContents);
...

Wir beenden unseren Test durch das Aufrufen des done()-Callbacks, sodass Mocha weiß, dass der Test dieses Falls gestoppt wird:

todos/index.test.js
...
done(err);
...

Wir geben das err-Objekt zu done(), sodass der Test mit Mocha fehlschlägt, falls ein Fehler vorhanden ist.

Speichern und beenden Sie index.test.js.

Wie zuvor führen wir diesen Test mit npm test durch. Ihre Konsole zeigt dann diese Ausgabe:

Output
... integrated test ✓ should be able to add and complete TODOs complete() ✓ should fail if there are no TODOs saveToFile() ✓ should save a single TODO 3 passing (15ms)

Sie haben nun Ihre erste asynchrone Funktion mit Mocha unter der Verwendung von Callbacks getestet. Zum Zeitpunkt des Schreibens dieses Tutorials sind Promises jedoch verbreiteter als Callbacks in neuem Node.js-Code, wie auch in unserem Artikel Schreiben von asynchronem Code in Node.js beschrieben. Als Nächstes lernen wir, wie wir auch diese mit Mocha testen können.

Promises

Ein Promise ist ein JavaScript-Objekt, das letztendlich einen Wert ausgibt. Wenn ein Promise erfolgreich ist, ist es gelöst. Wenn es auf einen Fehler trifft, wird es verworfen.

Wir ändern die saveToFile()-Funktion, damit sie Promises anstelle von Callbacks verwendet. Öffnen Sie index.js:

  1. nano index.js

Zuerst müssen wir ändern, wie das fs-Modul geladen ist. Ändern Sie in Ihrer index.js-Datei die require()-Aussage am Anfang der Datei, sodass sie aussieht wie folgt:

todos/index.js
...
const fs = require('fs').promises;
...

Wir haben nun das fs-Modul importiert, das Promises anstelle von Callbacks verwendet. Nun müssen wir einige Änderungen an saveToFile() vornehmen, damit es stattdessen mit Promises arbeitet.

Führen Sie in Ihrem Texteditor die folgenden Änderungen an der Funktion saveToFile() aus, um die Callbacks zu entfernen:

todos/index.js
...
saveToFile() {
    let fileContents = 'Title,Completed\n';
    this.todos.forEach((todo) => {
        fileContents += `${todo.title},${todo.completed}\n`
    });

    return fs.writeFile('todos.csv', fileContents);
}
...

Der erste Unterschied besteht darin, dass unsere Funktion keine Argumente mehr akzeptiert. Mit Promises benötigen wir keine Callback-Funktion. Die zweite Änderung betrifft die Weise, wie die Datei geschrieben ist. Wir geben nun das Ergebnis des writeFile()-Promises aus.

Speichern und schließen Sie index.js.

Wir passen unseren Test so an, dass er mit Promises funktioniert. Öffnen Sie index.test.js:

  1. nano index.test.js

Ändern Sie den saveToFile()-Test auf Folgendes:

todos/index.js
...
describe("saveToFile()", function() {
    it("should save a single TODO", function() {
        let todos = new Todos();
        todos.add("save a CSV");
        return todos.saveToFile().then(() => {
            assert.strictEqual(fs.existsSync('todos.csv'), true);
            let expectedFileContents = "Title,Completed\nsave a CSV,false\n";
            let content = fs.readFileSync("todos.csv").toString();
            assert.strictEqual(content, expectedFileContents);
        });
    });
});

Als erste Änderung müssen wir den done()-Callback aus den Argumenten entfernen. Wenn Mocha das done()-Argument durchläuft, muss es aufgerufen werden oder es gibt einen Fehler wie folgt aus:

1) saveToFile()
       should save a single TODO:
     Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/home/ubuntu/todos/index.test.js)
      at listOnTimeout (internal/timers.js:536:17)
      at processTimers (internal/timers.js:480:7)

Schließen Sie beim Testen von Promises den done()-Callback nicht in it() ein.

Um unser Promise zu testen, müssen wir unseren Assertionscode in die then()-Funktion einfügen. Beachten Sie, dass wir dieses Promise im Test ausgeben. Wir haben keine catch()-Funktion, um das Promise auszufangen, wenn es verworfen wird.

Wir geben das Promise aus, sodass alle Fehler, die in der then()-Funktion ausgegeben werden, in der it()-Funktion heraustreten. Wenn die Fehler nicht heraustreten, wird der Testfall mit Mocha nicht fehlschlagen. Beim Testen von Promises müssen Sie return auf das getestete Promise verwenden. Andernfalls besteht das Risiko, ein falsch-positives Ergebnis zu erhalten.

Wir lassen auch die catch()-Klausel aus, da Mocha erkennen kann, wenn ein Promise verworfen wird. Im Fall einer Verwerfung schlägt der Test automatisch fehl.

Da unser Test nun fertig ist, speichern und beenden Sie die Datei und führen Sie anschließend Mocha mit npm test aus. Als Bestätigung erhalten wir ein erfolgreiches Ergebnis:

Output
... integrated test ✓ should be able to add and complete TODOs complete() ✓ should fail if there are no TODOs saveToFile() ✓ should save a single TODO 3 passing (18ms)

Wir haben unseren Code und Test zur Verwendung von Promises geändert und wissen nun sicher, dass es funktioniert. Die neuesten asynchronen Muster verwenden jedoch async/await-Schlüsselwörter, damit wir nicht mehrere then()-Funktionen erstellen müssen, um erfolgreiche Ergebnisse zu bearbeiten. Sehen wir als Nächstes, wie wir mit async/await testen können.

async/await

Die Schlüsselwörter async/await erleichtern die Arbeit mit Promises, da sie nicht so ausführlich sind. Sobald wir eine Funktion als asynchron mit dem Schlüsselwort async definieren, können wir alle zukünftigen Ergebnisse in dieser Funktion mit dem Schlüsselwort await erhalten. Auf diese Weise können wir Promises verwenden, ohne die Funktionen then() oder catch() verwenden zu müssen.

Wir können unseren auf Promises basierenden saveToFile()-Test mit async/await vereinfachen. Führen Sie in Ihrem Texteditor diese kleineren Änderungen im saveToFile()-Test in der index.test.js aus:

todos/index.test.js
...
describe("saveToFile()", function() {
    it("should save a single TODO", async function() {
        let todos = new Todos();
        todos.add("save a CSV");
        await todos.saveToFile();

        assert.strictEqual(fs.existsSync('todos.csv'), true);
        let expectedFileContents = "Title,Completed\nsave a CSV,false\n";
        let content = fs.readFileSync("todos.csv").toString();
        assert.strictEqual(content, expectedFileContents);
    });
});

Die erste Änderung besteht darin, dass die von der it()-Funktion verwendete Funktion jetzt das Schlüsselwort async zur Definierung verwendet. Dadurch können wir das Schlüsselwort await in ihrem Körper verwenden.

Die zweite Änderung tritt auf, wenn wir saveToFile() aufrufen. Bevor es aufgerufen wird, wird das Schlüsselwort await verwendet. Node.js wird nun warten, bis diese Funktion gelöst ist, bevor es den Test fortsetzt.

Da wir den Code aus der then()-Funktion in den it()-Funktionskörper verschoben haben, ist unser Funktionscode leichter zu lesen. Die Ausführung dieses Codes mit npm test erzeugt diese Ausgabe:

Output
... integrated test ✓ should be able to add and complete TODOs complete() ✓ should fail if there are no TODOs saveToFile() ✓ should save a single TODO 3 passing (30ms)

Wir können jetzt asynchrone Funktionen testen, indem wir ein beliebiges von drei asynchronen Paradigmen entsprechend verwenden.

Wir haben mit dem Testen von synchronem und asynchronem Code mit Mocha schon einen breiten Bereich abgedeckt. Als Nächstes tauchen wir tiefer ein in einige andere Funktionalitäten, die Mocha bietet, um unsere Testerfahrung zu verbessern. Besonders interessant ist hierbei auch, wie Hooks die Testumgebungen verändern können.

Schritt 5 – Verwenden von Hooks zur Verbesserung von Testfällen

Hooks sind ein nützlicher Bestandteil von Mocha, der es uns ermöglicht, die Umgebung vor und nach einem Test zu konfigurieren. Wir fügen Hooks typischerweise in einen describe()-Funktionsblock, da diese eine für einige Testfälle spezifische Auf- und Abbaulogik enthalten.

Mocha bietet vier Hooks, die wir in unseren Tests verwenden können:

  • before: Dieser Hook wird einmal ausgeführt, bevor der erste Test beginnt.
  • beforeEach: Dieser Hook wird vor jedem Testfall ausgeführt.
  • after: Dieser Hook wird einmal ausgeführt, nachdem der letzte Testfall abgeschlossen ist.
  • afterEach: Dieser Hook wird nach jedem Testfall ausgeführt.

Hooks sind sehr nützlich, wenn wir eine Funktion oder Eigenschaft mehrmals testen, da sie uns erlauben, den Einrichtungscode des Tests (wie das Erstellen des todos-Objekts) vom Assertionscode des Tests zu trennen.

Um den Wert von Hooks zu sehen, fügen wir dem Testblock saveToFile() weitere Tests hinzu.

Obwohl wir bestätigt haben, dass wir unsere TODO-Elemente in eine Datei speichern können, haben wir nur ein Element gespeichert. Außerdem wurde das Element nicht als abgeschlossen markiert. Wir fügen weitere Tests hinzu, um sicherzustellen, dass die verschiedenen Aspekte unseres Moduls funktionieren.

Wir fügen zunächst einen zweiten Test hinzu, um zu bestätigen, dass unsere Datei korrekt gespeichert wird, wenn wir ein abgeschlossenes TODO-Element haben. Öffnen Sie Ihre Datei index.test.js mit Ihrem Texteditor:

  1. nano index.test.js

Ändern Sie den letzten Test folgendermaßen:

todos/index.test.js
...
describe("saveToFile()", function () {
    it("should save a single TODO", async function () {
        let todos = new Todos();
        todos.add("save a CSV");
        await todos.saveToFile();

        assert.strictEqual(fs.existsSync('todos.csv'), true);
        let expectedFileContents = "Title,Completed\nsave a CSV,false\n";
        let content = fs.readFileSync("todos.csv").toString();
        assert.strictEqual(content, expectedFileContents);
    });

    it("should save a single TODO that's completed", async function () {
        let todos = new Todos();
        todos.add("save a CSV");
        todos.complete("save a CSV");
        await todos.saveToFile();

        assert.strictEqual(fs.existsSync('todos.csv'), true);
        let expectedFileContents = "Title,Completed\nsave a CSV,true\n";
        let content = fs.readFileSync("todos.csv").toString();
        assert.strictEqual(content, expectedFileContents);
    });
});

Der Test ist ähnlich wie zuvor. Die wichtigsten Unterschiede sind, dass wir die complete()-Funktion vor saveToFile() aufrufen, und unsere expectedFileContents nun true anstatt false für den Wert der completed-Kolumne haben.

Speichern und schließen Sie die Datei.

Wir führen unseren neuen Test und alle anderen mit npm test aus:

  1. npm test

Dadurch ergibt sich Folgendes:

Output
... integrated test ✓ should be able to add and complete TODOs complete() ✓ should fail if there are no TODOs saveToFile() ✓ should save a single TODO ✓ should save a single TODO that's completed 4 passing (26ms)

Es funktioniert wie erwartet. Es gibt jedoch Raum für Verbesserungen. Sie müssen ein Todos-Objekt zu Beginn des Tests instanziieren. Beim Hinzufügen von mehr Testfällen wird dies schnell repetitiv und vergeudet Speicherplatz. Außerdem erstellt der Test bei jeder Ausführung eine Datei. Das kann von jemandem, der sich mit dem Modul nicht so gut auskennt, mit einer tatsächlichen Ausgabe verwechselt werden. Es wäre schön, wenn wir unsere Ausgabedateien nach dem Testen bereinigen würden.

Führen wir nun diese Verbesserungen mit Test-Hooks aus. Wir verwenden den Hook beforeEach(), um unsere Testvorrichtung von TODO-Elementen einzurichten. Eine Testvorrichtung ist jeder konsistente Zustand, der in einem Test verwendet wird. In unserem Fall ist unsere Testvorrichtung ein neues todos-Objekt, dem bereits ein TODO-Element hinzugefügt wurde. Wir verwenden afterEach(), um die vom Test erstellte Datei zu entfernen.

Führen Sie in index.test.js die folgenden Änderungen an Ihrem letzten Test für saveToFile() aus:

todos/index.test.js
...
describe("saveToFile()", function () {
    beforeEach(function () {
        this.todos = new Todos();
        this.todos.add("save a CSV");
    });

    afterEach(function () {
        if (fs.existsSync("todos.csv")) {
            fs.unlinkSync("todos.csv");
        }
    });

    it("should save a single TODO without error", async function () {
        await this.todos.saveToFile();

        assert.strictEqual(fs.existsSync("todos.csv"), true);
        let expectedFileContents = "Title,Completed\nsave a CSV,false\n";
        let content = fs.readFileSync("todos.csv").toString();
        assert.strictEqual(content, expectedFileContents);
    });

    it("should save a single TODO that's completed", async function () {
        this.todos.complete("save a CSV");
        await this.todos.saveToFile();

        assert.strictEqual(fs.existsSync('todos.csv'), true);
        let expectedFileContents = "Title,Completed\nsave a CSV,true\n";
        let content = fs.readFileSync("todos.csv").toString();
        assert.strictEqual(content, expectedFileContents);
    });
});

Entschlüsseln wir alle vorgenommenen Änderungen. Wir haben dem Testblock ein beforeEach() hinzugefügt:

todos/index.test.js
...
beforeEach(function () {
    this.todos = new Todos();
    this.todos.add("save a CSV");
});
...

Diese beiden Zeilen Code erstellen ein neues Todos-Objekt, das in jedem unserer Tests verfügbar ist. Mit Mocha verweist das this-Objekt in beforeEach() auf dasselbe this-Objekt in it(). this ist für jeden Codeblock im describe()-Block gleich. Weitere Informationen über this finden Sie in unserem Tutorial Verstehen Sie This, Bind, Call und Apply in JavaScript.

Diese leistungsstarke gemeinsame Nutzung des Kontexts ist der Grund, warum wir schnell Testvorrichtungen erstellen können, die für unsere beiden Tests funktionieren.

Dann bereinigen wir unsere CSV-Datei in der afterEach()-Funktion:

todos/index.test.js
...
afterEach(function () {
    if (fs.existsSync("todos.csv")) {
        fs.unlinkSync("todos.csv");
    }
});
...

Wenn unser Test fehlgeschlagen ist, hat der Test möglicherweise keine Datei erstellt. Aus diesem Grund überprüfen wir, ob die Datei vorhanden ist, bevor wir die Funktion unlinkSync() verwenden, um diese zu löschen.

Die verbleibenden Änderungen wechseln die Referenz von todos, die zuvor in der Funktion it() erstellt wurden, zu this.todos, das im Mocha-Kontext verfügbar ist. Wir haben auch die Zeilen gelöscht, die zuvor todos in den einzelnen Testfällen instanziierten.

Führen wir nun diese Datei aus, um zu bestätigen, dass unsere Tests noch funktionieren. Geben Sie npm test in Ihr Terminal ein, um Folgendes zu erhalten:

Output
... integrated test ✓ should be able to add and complete TODOs complete() ✓ should fail if there are no TODOs saveToFile() ✓ should save a single TODO without error ✓ should save a single TODO that's completed 4 passing (20ms)

Die Ergebnisse sind gleich, und als zusätzlichen Vorteil haben wir die Einrichtungszeit für neue Tests für die Funktion saveToFile() leicht reduziert sowie eine Lösung für die zurückbleibende CSV-Datei gefunden.

Zusammenfassung

In diesem Tutorial haben Sie ein Node.js-Modul geschrieben, um TODO-Elemente zu verwalten und den Code manuell mit der Node.js-REPL getestet. Dann haben Sie eine Testdatei erstellt und das Mocha-Framework zur Ausführung automatisierter Tests verwendet. Mit dem assert-Modul konnten Sie verifizieren, ob Ihr Code funktioniert. Sie haben mit Mocha auch synchrone und asynchrone Funktionen getestet. Schließlich haben Sie Hooks mit Mocha erstellt, die das Schreiben mehrerer verwandter Testfälle wesentlich lesbarer und wartungsfreundlicher machen.

Mit diesen neuen Kenntnissen können Sie nun versuchen, Tests für neue Node.js-Module zu schreiben, die Sie gerade erstellen. Können Sie über die Ein- und Ausgaben Ihrer Funktion nachdenken und Ihren Test schreiben, bevor Sie Ihren Code erstellen?

Wenn Sie weitere Informationen über das Mocha-Framework erhalten möchten, besuchen Sie unsere offizielle Mocha-Dokumentation. Wenn Sie gerne noch mehr über Node.js lernen möchten, können Sie zu der Serienseite Codieren in Node.js zurückkehren.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors

Default avatar

Senior Technical Editor

Editor at DigitalOcean, fiction writer and podcaster elsewhere, always searching for the next good nautical pun!


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Featured on Community

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more