Zum Hauptinhalt springen

🧪 Testgetriebene Entwicklung (TDD)

📖 Definition

TDD (Test-Driven Development) ist eine Entwicklungsmethodik, bei der Sie zuerst Tests schreiben und dann Code schreiben, um diese Tests zu bestehen. Es folgt dem Rot-Grün-Refactor-Zyklus, verbessert die Codequalität, reduziert Fehler und macht Refactoring sicherer. Unit-Tests verifizieren einzelne Funktionen oder Komponenten unabhängig.

🎯 Einfache Analogie

Bauplan Zuerst

Traditionelle Entwicklung
1. Haus bauen
2. Nach Fertigstellung prüfen
3. Probleme finden → Große Korrekturen nötig
4. Kosten steigen

TDD
1. Bauplan zeichnen (Test schreiben)
2. Nach Bauplan bauen (Code schreiben)
3. Verifizieren (Test ausführen)
4. Verbessern (Refactoring)
5. Sicher und genau

⚙️ Funktionsweise

TDD-Zyklus (Rot-Grün-Refactor)

🔴 Rot (Fehlschlag)
└─ Test schreiben → Fehlschlag (kein Code)

🟢 Grün (Erfolg)
└─ Minimalen Code schreiben → Test besteht

🔵 Refactor (Verbessern)
└─ Code verbessern → Test besteht noch

Wiederholen → Schrittweise Verbesserung

💡 Wichtige Beispiele

Grundlegendes TDD-Beispiel

// ========== 1. Rot: Test schreiben (fehlschlägt) ==========
test('add-Funktion addiert zwei Zahlen', () => {
expect(add(2, 3)).toBe(5);
});
// FAIL - add ist nicht definiert

// ========== 2. Grün: Minimaler Code (besteht) ==========
function add(a, b) {
return a + b;
}
// PASS ✅

// ========== 3. Refactor: Verbessern (falls nötig) ==========
// Code ist einfach, keine Verbesserung nötig

Unit-Test mit Jest

// user.js
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}

isValidEmail() {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(this.email);
}
}

// user.test.js
describe('User', () => {
let user;

beforeEach(() => {
user = new User('Hans', 'hans@example.com');
});

test('sollte true für gültige E-Mail zurückgeben', () => {
expect(user.isValidEmail()).toBe(true);
});

test('sollte false für ungültige E-Mail zurückgeben', () => {
user.email = 'invalid-email';
expect(user.isValidEmail()).toBe(false);
});
});

Asynchrone Tests

test('async-Funktion gibt Daten zurück', async () => {
const data = await fetchData();
expect(data).toEqual({ name: 'John' });
});

Mocking

// Funktions-Mock
const mockCallback = jest.fn(x => x * 2);

[1, 2, 3].forEach(mockCallback);

expect(mockCallback).toHaveBeenCalledTimes(3);

// Modul-Mock
jest.mock('axios');

test('fetchUser gibt Benutzerdaten zurück', async () => {
axios.get.mockResolvedValue({ data: { id: 1 } });
const user = await fetchUser(1);
expect(user).toEqual({ id: 1 });
});

🤔 Häufig gestellte Fragen

F1. Vorteile von TDD?

A:

Vorteile:
1. Weniger Fehler
2. Sicheres Refactoring
3. Bessere Codequalität
4. Dokumentation
5. Vertrauen

Nachteile:
1. Anfänglicher Zeitaufwand
2. Lernkurve
3. Test-Wartung

Fazit: Langfristiger Gewinn

F2. Was Testen?

A:

// ✅ Sollte getestet werden
1. Geschäftslogik
2. Grenzfälle
3. Fehlerbehandlung
4. Öffentliche APIs

// ❌ Nicht testen müssen
1. Externe Bibliotheken
2. Einfache Getter/Setter
3. Private Implementierungsdetails

F3. Test-Abdeckungsziel?

A:

# Abdeckung messen
npm test -- --coverage

# Ziel:
80%+ Abdeckung // Realistisches Ziel
100% Abdeckung // Ideal aber unpraktisch

# Denken Sie daran:
Hohe Abdeckung ≠ Gute Tests
Aussagekräftige Tests sind wichtig!

🎬 Zusammenfassung

TDD ist die Grundlage der Softwarequalität:

  • Rot-Grün-Refactor: Kern-TDD-Zyklus
  • Unit-Tests: Schnell und isoliert
  • Test-First: Tests treiben Design
  • Kontinuierliche Verbesserung: Sicherheitsnetz für Refactoring

Tests sind eine Investition in Ihr zukünftiges Selbst! 🧪✨