Code, der nicht getestet wird, ist im Grunde schon fehlerhaft – Sie wissen es nur noch nicht. Professionelles Testing gibt Ihnen die nötige Sicherheit, um Code ohne Angst zu refaktorieren, neue Features schneller auszuliefern und nachts ruhig zu schlafen. In diesem Leitfaden zeigen wir Ihnen, wie Sie moderne JavaScript-Anwendungen fehlerfrei und effizient testen.
Die verschiedenen Testarten im Überblick
- Unit Tests (Komponententests): Isolierte Überprüfung einzelner Funktionen oder Module. Sie sind extrem schnell auszuführen, kostengünstig und bilden das Fundament Ihrer Testsuite.
- Integrationstests: Prüfen das Zusammenspiel mehrerer Komponenten oder Module, wie beispielsweise Datenbankabfragen, API-Endpunkte oder komplexe UI-Interaktionen.
- End-to-End (E2E) Tests: Simulieren echtes Nutzerverhalten im Browser. Vom Klick auf einen Button bis zum Absenden eines Formulars wird die gesamte Anwendung auf Herz und Nieren geprüft.
/ E2E-Tests \ ← Wenige (langsam, teuer)
/ Integrationstests \ ← Einige
/ Unit Tests \ ← Viele (schnell, günstig)
Erste Schritte mit Jest
Jest ist das beliebteste Test-Framework im JavaScript-Ökosystem. Es ist schnell, bietet out-of-the-box Code-Coverage-Berichte und eine intuitive Syntax.
# Jest installieren
npm install -D jest
# Für TypeScript-Unterstützung
npm install -D jest ts-jest @types/jest
# Testskripte in der package.json hinterlegen
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
Ihren ersten Test schreiben
Lassen Sie uns eine einfache mathematische Hilfsfunktion erstellen und diese direkt mit Jest testen, um die Funktionsweise zu demonstrieren.
export function add(a, b) {
return a + b;
}
export function divide(a, b) {
if (b === 0) throw new Error('Cannot divide by zero');
return a / b;
}
import { add, divide } from './math';
describe('add', () => {
test('adds two positive numbers', () => {
expect(add(2, 3)).toBe(5);
});
test('handles negative numbers', () => {
expect(add(-1, -1)).toBe(-2);
});
test('handles zero', () => {
expect(add(0, 5)).toBe(5);
});
});
describe('divide', () => {
test('divides two numbers', () => {
expect(divide(10, 2)).toBe(5);
});
test('throws on division by zero', () => {
expect(() => divide(10, 0)).toThrow('Cannot divide by zero');
});
});
Wichtige Jest Matcher im Alltag
Jest bietet eine Vielzahl von sogenannten Matchern, mit denen Sie Werte auf unterschiedliche Weise überprüfen können.
// Gleichheit prüfen
expect(value).toBe(42); // Strikte Gleichheit (===)
expect(obj).toEqual({ a: 1, b: 2 }); // Tiefe Gleichheit (Deep Equality)
// Wahrheitswerte prüfen
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeUndefined();
expect(value).toBeDefined();
// Numerische Werte
expect(value).toBeGreaterThan(3);
expect(value).toBeLessThanOrEqual(10);
expect(0.1 + 0.2).toBeCloseTo(0.3); // Wichtig bei Fließkommazahlen
// Zeichenketten (Regex & Substrings)
expect(str).toMatch(/regex/);
expect(str).toContain('substring');
// Arrays & Listen
expect(arr).toContain('item');
expect(arr).toHaveLength(3);
// Objekte analysieren
expect(obj).toHaveProperty('name');
expect(obj).toMatchObject({ name: 'Alice' });
Mocking: Externe Abhängigkeiten simulieren
Beim Testen möchten Sie oft keine echten API-Aufrufe oder Datenbankabfragen durchführen. Hier kommt Mocking ins Spiel, um das Verhalten externer Module kontrolliert zu simulieren.
// Eine Funktion mocken
const mockFn = jest.fn();
mockFn.mockReturnValue(42);
mockFn.mockResolvedValue({ id: 1 }); // Für asynchrone Promises
// Aufrufe validieren
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledWith('arg1', 'arg2');
expect(mockFn).toHaveBeenCalledTimes(3);
// Ein komplettes Modul mocken
jest.mock('./database', () => ({
getUser: jest.fn().mockResolvedValue({ id: 1, name: 'Alice' }),
saveUser: jest.fn().mockResolvedValue(true),
}));
Asynchronen JavaScript-Code testen
Moderne JavaScript-Anwendungen basieren stark auf asynchronen Operationen (Promises, async/await). Jest macht das Testen dieser Abläufe kinderleicht und transparent.
// async/await (empfohlener Standard)
test('fetches user data', async () => {
const user = await getUser(1);
expect(user.name).toBe('Alice');
});
// Fehlerhafte Promises (Rejections) testen
test('throws for invalid ID', async () => {
await expect(getUser(-1)).rejects.toThrow('Invalid ID');
});
Testgetriebene Entwicklung (TDD)
Test-Driven Development (TDD) ist ein Entwicklungsansatz, der auf einem kurzen, sich ständig wiederholenden Arbeitszyklus basiert:
- Rot (Red): Schreiben Sie zuerst einen fehlschlagenden Test für ein noch nicht existierendes Feature.
- Grün (Green): Implementieren Sie nur so viel Code, dass der Test erfolgreich durchläuft.
- Refactoring (Refactor): Optimieren Sie den Code (Sauberkeit, Performance), während Sie sicherstellen, dass alle Tests grün bleiben.
TDD zwingt Sie dazu, die Schnittstellen und das Verhalten Ihrer Software aus Sicht des Anwenders zu gestalten, bevor Sie mit der eigentlichen Implementierung beginnen. Das Ergebnis ist sauberer, modularer Code.
Best Practices für saubere Tests
- Verhalten testen, nicht die Implementierung. Wenn Sie den internen Code einer Funktion umstrukturieren, sollten Ihre Tests nicht fehlschlagen, solange das Ergebnis identisch bleibt.
- Eine Assertion pro Testfokus. Jeder Test sollte sich auf ein bestimmtes Szenario konzentrieren, um Fehlerquellen sofort lokalisieren zu können.
- Aussagekräftige Testnamen verwenden. Ein Name wie
"sollte einen 404-Fehler werfen, wenn der Nutzer nicht existiert"ist deutlich hilfreicher als"getUser Test". - Gute Code-Coverage anstreben. Versuchen Sie, etwa 80% Abdeckung zu erreichen. Eine 100-prozentige Abdeckung zu erzwingen, führt oft zu ineffizienten Tests ohne echten Mehrwert.
- Automatisierung per CI/CD. Integrieren Sie Ihre Testsuite in Ihre Continuous-Integration-Pipeline (z.B. GitHub Actions), damit jeder Pull Request vor dem Merge validiert wird.
- Performance im Blick behalten. Langsame Tests werden ungern ausgeführt. Nutzen Sie Mocks für zeitintensive Operationen wie Netzwerk-Requests.
Nutzen Sie unsere kostenlosen JavaScript-Tools
Optimieren, komprimieren und formatieren Sie Ihren JavaScript-Code im Handumdrehen.