Skip to main content

So schreiben Sie Ihren ersten Unit-Test in JavaScript

Artikel von

Raphael Mun

feature java dto

28. Oktober 2022

0 Min. Lesezeit

Das Testen von Code ist der erste Schritt, um ihn sicherer zu machen. Eine der besten Methoden dafür sind Unit-Tests. Sie stellen sicher, dass jede kleinere Funktion innerhalb einer App wie vorgesehen arbeitet – insbesondere, wenn die App Grenzfälle, ungültige oder potenziell schädliche Eingaben erhält.

Warum sollten Sie Unit-Tests durchführen?

Für Unit-Tests gibt es viele verschiedene Ansätze. Zu den wichtigsten Zwecken von Unit-Tests gehören:

  • Funktionalität überprüfen: Unit-Tests stellen sicher, dass Code das Richtige tut und nichts tut, was er nicht soll – genau dort treten die meisten Bugs auf.

  • Code-Regression verhindern: Wenn wir einen Bug finden, verhindert ein zusätzlicher Unit-Test für das betreffende Szenario, dass Codeänderungen den Bug in Zukunft erneut einführen.

  • Code dokumentieren: Mit den passenden Unit-Tests beschreiben eine vollständige Testsuite und ihre Ergebnisse, wie eine App funktionieren soll.

  • Ihre App absichern: Unit-Tests können auf ausnutzbare Schwachstellen prüfen, etwa solche, die schädliche SQL-Injections ermöglichen.

Umfang und Erstellung von Unit-Tests

Mit einem Unit-Test-Framework können wir Tests schnell schreiben, automatisieren und in unsere Entwicklungs- und Deployment-Prozesse integrieren. Diese Frameworks unterstützen häufig Tests sowohl für JavaScript-Code im Frontend als auch im Backend.

Im Folgenden finden Sie einige allgemeine Leitlinien, die Ihnen dabei helfen, performante Unit-Tests und testbaren Code zu schreiben.

Halten Sie Unit-Tests kurz und einfach

Überfrachten Sie Ihre Unit-Tests nicht. Ein Test sollte nur aus wenigen Codezeilen bestehen und kurze, funktionale Teile Ihrer Anwendung prüfen.

Berücksichtigen Sie positive und negative Testfälle

Ein Test, der bestätigt, dass eine Funktion korrekt ausgeführt wird, ist hilfreich. Noch effektiver ist jedoch eine größere Auswahl an Tests, die prüfen, ob eine Funktion bei falscher Verwendung oder in Grenzfällen wie vorgesehen fehlschlägt. Diese negativen Tests können noch wertvoller sein, da sie dabei helfen, unerwartete Szenarien vorherzusehen – etwa wann eine Funktion eine Ausnahme auslösen oder wie sie mit fehlerhaft formatierten Daten umgehen sollte.

Teilen Sie lange und komplexe Funktionen auf

Große Funktionen mit viel Logik sind schwer zu testen. Zu viele Operationen erschweren es, jede Variable effektiv zu prüfen. Ist eine Funktion zu komplex, teilen Sie sie in kleinere Funktionen auf, die einzeln getestet werden können.

Vermeiden Sie Netzwerk- und Datenbankverbindungen

Unit-Tests sollten schnell und ressourcenschonend sein. Funktionen, die Netzwerkaufrufe ausführen oder Verbindungen zu anderen Apps oder Prozessen herstellen, benötigen jedoch viel Zeit. Dadurch wird es schwieriger, viele Operationen gleichzeitig auszuführen, und der Code kann anfälliger werden. Mit Mocking in Unit-Tests können Sie Netzwerk- oder Datenbankaufrufe simulieren und so den Rest der Funktion testen. Die tatsächlichen Netzwerk- und Datenbankverbindungen können Sie in einem anderen Testverfahren namens Integrationstests einbeziehen.

So schreiben Sie Unit-Tests

Nachdem Sie nun einige Best Practices für Unit-Tests kennengelernt haben, können Sie Ihren ersten Unit-Test in JavaScript schreiben.

In diesem Tutorial verwenden wir das Mocha-Framework, eines der beliebtesten Frameworks für Unit-Tests. Jedes Test-Framework unterscheidet sich ein wenig von den anderen, doch die Konzepte sind ähnlich genug, dass Sie nach dem Erlernen der Grundlagen leicht zwischen ihnen wechseln können.

Für diese Anleitung muss Node.js auf Ihrem Computer installiert sein.

Ein neues Projekt erstellen

Öffnen Sie zunächst in einem neuen Projektordner ein Terminalfenster oder eine Eingabeaufforderung. Erstellen Sie darin ein neues Node.js-Projekt, indem Sie npm init -y eingeben.

Dadurch wird im Ordner eine Datei namens package.json erstellt. Anschließend können Sie Mocha mit npm install -D mocha als Entwicklungsabhängigkeit installieren.

Öffnen Sie als Nächstes die Datei package.json in Ihrem Code-Editor und ersetzen Sie das Platzhalter-Testskript durch mocha:

  "scripts": {
    "test": "mocha"
  },

Eine Klasse implementieren

Schreiben Sie als Nächstes ein einfaches Ampelsystem, das Sie mit Unit-Tests prüfen können.

Erstellen Sie in Ihrem Projektverzeichnis eine Datei namens traffic.js und fügen Sie den folgenden Code für eine Klasse namens TrafficLight ein:

class TrafficLight {
    constructor() {
        this.lightIndex = 0;
    }

    static get colors() {
        return [ "green", "yellow", "red" ];
    }
    get light() {
        return TrafficLight.colors[ this.lightIndex ];
    }
    next() {
        this.lightIndex++;
        // This is intentionally wrong!
        if( this.lightIndex > TrafficLight.colors.length ) {
            this.lightIndex = 0;
        }
    }
}

module.exports = TrafficLight;

Diese Klasse besteht aus vier Teilen:

  • TrafficLight.colors: eine konstante Eigenschaft für die Ampelfarben.

  • lightIndex: eine Variable, die den Index der aktuellen Ampelfarbe speichert.

  • light: eine Klasseneigenschaft, die die aktuelle Ampelfarbe als Zeichenfolge zurückgibt.

  • next(): eine Funktion, die die Ampel auf die nächste Farbe umschaltet.

Den ersten Unit-Test konfigurieren und hinzufügen

Jetzt ist es an der Zeit, dem Code Unit-Tests hinzuzufügen.

Erstellen Sie im Projektverzeichnis einen Ordner namens test. Standardmäßig sucht Mocha hier nach Unit-Tests. Legen Sie anschließend im neuen Testordner eine Datei namens traffic.test.js an.

Importieren Sie als Nächstes oben in der Datei die Klasse TrafficLight:

const TrafficLight = require( "../traffic" );

Für die Tests verwenden wir auch das Modul assert. Binden Sie es daher in Ihrem Code ein:

const assert = require( "assert" );

Mit Mocha können wir Unit-Tests mithilfe der Funktion describe() in Gruppen zusammenfassen. Für diese Klasse können wir also eine Gruppe auf oberster Ebene wie folgt erstellen:

describe( "TrafficLight", function () {
});

Erstellen wir nun in einer eigenen Untergruppe namens colors innerhalb der Gruppe TrafficLight Unit-Tests, um die Ampelfarben zu überprüfen:

describe( "TrafficLight", function () {
    describe( "colors", function () {
    });
});

Mit diesem ersten Unit-Test können wir überprüfen, dass colors nur drei Zustände hat: Grün, Gelb und Rot. Der Test wird mit einer Funktion it() innerhalb der Gruppe describe() definiert. Schreiben Sie den Test also wie folgt:

describe( "TrafficLight", function () {
    describe( "colors", function () {
        it( "has 3 states", function () {
            const traffic = new TrafficLight();
            assert.equal( 3, TrafficLight.colors.length );
        });
    });
});

Führen wir nun den Unit-Test aus und prüfen, ob er erfolgreich ist.

Führen Sie npm test in Ihrem Terminalfenster aus. Wenn alles korrekt ist, zeigt Mocha die Ergebnisse des Testlaufs an.

Terminalausgabe mit Mocha-Tests für die Farben einer Ampel, wobei ein Test in 8 ms bestanden wird.

Weitere Unit-Tests hinzufügen

Unser Projekt ist jetzt für Unit-Tests bereit. Wir können also weitere Tests hinzufügen, um sicherzustellen, dass unser Code korrekt funktioniert.

Fügen Sie zunächst der Gruppe colors einen Unit-Test hinzu, der überprüft, ob die Ampelfarben korrekt und in der richtigen Reihenfolge aufgeführt sind. Eine Möglichkeit, diesen Test zu implementieren, sieht so aus:

it( "colors are in order", function () {
    const expectedLightOrder = [ "green", "yellow", "red" ];
    const traffic = new TrafficLight();
    for( let i = 0; i < expectedLightOrder.length; i++ ) {
        assert.equal( expectedLightOrder[ i ], TrafficLight.colors[ i ] );
    }
});

Testen Sie als Zweites die Funktion next(), um zu prüfen, ob sie die Ampelfarben korrekt wechselt. Erstellen Sie eine neue Untergruppe und fügen Sie zwei Unit-Tests hinzu: Der eine prüft, ob die Farben in der richtigen Reihenfolge wechseln, der andere, ob die Ampel nach red wieder zu green wechselt.

Schreiben Sie die Unit-Tests wie folgt:

describe( "next()", function () {
    it( "changes lights in order", function () {
        const traffic = new TrafficLight();
        for( let i = 0; i < TrafficLight.colors.length; i++ ) 
            assert.equal( traffic.light, TrafficLight.colors[ i ] );
            traffic.next();
        }
    });
    it( "loops back to green", function () {
        const traffic = new TrafficLight();
        // Change the light 3x to go from green -> yellow -> red -> green
        for( let i = 0; i < 3; i++ ) {
            traffic.next();
        }
        assert.equal( traffic.light, TrafficLight.colors[ 0 ] );
    });
});

Wenn wir die Tests jetzt erneut ausführen, sehen wir, dass einer davon fehlschlägt. Der Grund dafür ist ein Bug in der Klasse TrafficLight.

Terminalausgabe von Mocha-JavaScript-Tests mit drei bestandenen Tests und einer fehlgeschlagenen Ampel-Assertion.

Den Bug beheben

Öffnen Sie den Code der Klasse TrafficLight erneut und suchen Sie den Kommentar in der Funktion next(), der lautet: // This is intentionally wrong!

Anhand unserer Unit-Tests wissen wir, dass diese Funktion nicht korrekt zu green zurückwechselt. Der Code prüft derzeit, ob der Wert von lightIndex die Anzahl der Ampelfarben überschreitet. Der Index beginnt jedoch bei 0. Stattdessen müssen wir zum Anfang zurückkehren, sobald der Wert genau der Anzahl der Farben entspricht. Aktualisieren wir den Code so, dass der Wert von lightIndex wieder auf 0 gesetzt wird, wenn er gleich der Länge der Liste mit Ampelfarben ist:

// This is intentionally wrong!
if( this.lightIndex === TrafficLight.colors.length ) {
    this.lightIndex = 0;
}

Jetzt sollten alle Ihre Unit-Tests erfolgreich sein. Und das Beste daran: Selbst wenn die Klasse TrafficLight umgestaltet oder umfassend geändert wird, erkennen unsere Unit-Tests diesen Bug, bevor er die Nutzer erreicht.

Terminalausgabe mit Mocha-Unit-Tests für die Farben einer Ampel, mit vier bestandenen Tests.

Zusammenfassung

Unit-Tests lassen sich einfach einrichten und sind ein wirksames Werkzeug für die Softwareentwicklung. Sie helfen dabei, Bugs frühzeitig zu beheben und zu verhindern, dass sie erneut auftreten. So bleiben Projekte leichter überschaubar und wartbar, auch wenn sie größer und komplexer werden – insbesondere in größeren Entwicklungsteams. Automatisierte Tests wie diese ermöglichen es Entwicklern außerdem, ihren Code umzugestalten und zu optimieren, ohne sich Sorgen machen zu müssen, ob der neue Code korrekt funktioniert.

Unit-Tests sind ein wichtiger Bestandteil Ihres Entwicklungsprozesses und entscheidend dafür, bessere und sicherere JavaScript-Apps zu entwickeln.

Viel Erfolg beim Testen!

Starten Sie mit Capture the Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.