Tests prüfen automatisch, ob dein Code das Richtige tut. Sie sind das Sicherheitsnetz, mit dem du Änderungen wagen kannst.
Der eingebaute Testrunner von Node.js
Seit Node.js 20 gibt es node:test und node:assert, ohne zusätzliche Pakete:
const test = require("node:test");
const assert = require("node:assert/strict");
function istPalindrom(text) {
const sauber = text.toLowerCase().replace(/[^a-z0-9]/g, "");
return sauber === [...sauber].reverse().join("");
}
test("erkennt Palindrome", () => {
assert.equal(istPalindrom("Anna"), true);
assert.equal(istPalindrom("Ein Neger mit Gazelle zagt im Regen nie"), true);
});
test("erkennt Nicht-Palindrome", () => {
assert.equal(istPalindrom("Hallo"), false);
});
test("leerer Text ist ein Palindrom", () => {
assert.equal(istPalindrom(""), true);
});✔ erkennt Palindrome (0.9ms)
✔ erkennt Nicht-Palindrome (0.2ms)
✔ leerer Text ist ein Palindrom (0.2ms)
ℹ tests 3
ℹ pass 3
ℹ fail 0Starte alle Tests mit node --test. Dateien mit Namen wie *.test.js werden automatisch gefunden.
assert-Methoden
| Methode | Prüft |
|---|---|
assert.equal(a, b) | strikte Gleichheit (im strict-Modul) |
assert.deepEqual(a, b) | gleiche Struktur bei Objekten/Arrays |
assert.ok(x) | truthy |
assert.throws(fn, Fehler) | wirft den Fehler |
await assert.rejects(promise) | Promise wird abgelehnt |
assert.match(text, /regex/) | passt zum Muster |
const assert = require("node:assert/strict");
assert.deepEqual({ a: [1, 2] }, { a: [1, 2] });
assert.throws(() => JSON.parse("x"), SyntaxError);
try {
assert.equal(1 + 1, 3, "Rechnen klappt nicht");
} catch (e) {
console.log(e.name, "-", e.message.split("\n")[0]);
}
console.log("deepEqual und throws bestanden");AssertionError - Rechnen klappt nicht deepEqual und throws bestanden
Aufbau eines guten Tests
Ein Test hat drei Teile (Arrange – Act – Assert):
- Arrange: Daten vorbereiten
- Act: die zu testende Funktion aufrufen
- Assert: das Ergebnis prüfen
Gute Tests sind klein, unabhängig voneinander, schnell und beschreiben im Namen, was erwartet wird.
Was testet man?
- Normale Fälle
- Grenzfälle: leer, 0, negativ, sehr groß,
null,undefined - Fehlerfälle: Was passiert bei falscher Eingabe?
- Jeden gefundenen Fehler zuerst als fehlschlagenden Test festhalten, dann beheben
Asynchronen Code testen
const test = require("node:test");
const assert = require("node:assert/strict");
const lade = async (id) => {
await new Promise(r => setTimeout(r, 10));
if (id < 0) throw new RangeError("ungültige ID");
return { id };
};
test("lädt einen Datensatz", async () => {
assert.deepEqual(await lade(5), { id: 5 });
});
test("lehnt negative IDs ab", async () => {
await assert.rejects(lade(-1), RangeError);
});✔ lädt einen Datensatz (12ms)
✔ lehnt negative IDs ab (11ms)
ℹ tests 2
ℹ pass 2
ℹ fail 0Mocks und Fakes
Wenn dein Code Netzwerk, Zeit oder Zufall benutzt, ersetzt du diese Teile im Test durch kontrollierbare Varianten:
const test = require("node:test");
const assert = require("node:assert/strict");
test("zählt Aufrufe mit einer Mock-Funktion", (t) => {
const log = t.mock.fn();
[1, 2, 3].forEach(x => log(x));
assert.equal(log.mock.callCount(), 3);
assert.deepEqual(log.mock.calls[0].arguments, [1]);
});✔ zählt Aufrufe mit einer Mock-Funktion (1ms)
ℹ tests 1
ℹ pass 1
ℹ fail 0Ein häufiger Trick: Übergib Abhängigkeiten als Parameter (Dependency Injection), dann kannst du im Test eine Fake-Variante hineingeben.
Andere Testwerkzeuge
| Werkzeug | Besonderheit |
|---|---|
| Vitest | schnell, Jest-ähnlich, passt gut zu Vite |
| Jest | weit verbreitet, viele Matcher |
| Playwright | End-to-End-Tests im echten Browser |
| Testing Library | testet UI-Komponenten aus Nutzersicht |
Alle folgen demselben Prinzip: test("beschreibung", () => { expect(...)... }).
Testabdeckung
node --test --experimental-test-coverage zeigt, welche Zeilen Tests durchlaufen. 100 % Abdeckung heißt nicht fehlerfrei, aber wenig Abdeckung zeigt Lücken.
Merke
- Tests prüfen Code automatisch (
node --test, Vitest, Jest) - Arrange – Act – Assert; klein, unabhängig, sprechende Namen
- Normalfälle, Grenzfälle und Fehlerfälle testen
- Async-Tests mit
async/awaitundassert.rejects - Abhängigkeiten über Parameter hereingeben, damit sie sich ersetzen lassen
Aufgabe
Schreibe eine Funktion summe(liste) und mindestens vier Tests, darunter leere Liste und Fehler bei Nicht-Zahlen.