Software-Testing

KAPITEL 04 · PROGRAMMIERUNG

Software-Testing

Qualitätssicherung durch systematisches Testen – von Unit-Tests über Integrationstests bis zu TDD und BDD. Lernen Sie, wie Sie hochwertigen, zuverlässigen Code schreiben und Fehler frühzeitig erkennen.

Unit-Tests Integration TDD & BDD Testabdeckung

Inhaltsverzeichnis

Schnellübersicht

Auf dieser Seite lernen Sie alles über Software-Testing:

  • Definition: Was ist Software-Testing und warum ist es wichtig?
  • Testarten: Unit, Integration, System, Acceptance
  • Testmethoden: Black Box, White Box, Gray Box
  • Teststufen: Das V-Modell
  • Testtechniken: Äquivalenzklassen, Grenzwerte, etc.
  • TDD & BDD: Test-Driven Development & Behavior-Driven Development
  • Code-Beispiele: Unit-Tests in Python, Java, JavaScript
  • Testabdeckung: Coverage-Metriken verstehen
  • Test-Tools: JUnit, pytest, Jest, Selenium
  • Best Practices: Qualität, Automatisierung, Dokumentation
  • FAQ: Häufige Fragen & Antworten

1. Was ist Software-Testing?

Definition

Software-Testing (Software-Testen) ist der Prozess der systematischen Überprüfung einer Software, um Fehler (Bugs) zu finden, die Korrektheit zu validieren und sicherzustellen, dass die Software den Anforderungen entspricht. Testing ist ein wesentlicher Bestandteil der Software-Qualitätssicherung.

Im professionellen Software-Engineering ist Testing unverzichtbar für: Frühes Erkennen von Fehlern (kostet weniger als späte Fixes), Sicherstellung der Code-Qualität, Dokumentation des erwarteten Verhaltens, Vermeidung von Regressionen bei Änderungen und Aufbau von Vertrauen in die Software.

Die drei Hauptziele von Testing: Fehler finden (Defect Detection), Qualität sicherstellen (Quality Assurance) und Vertrauen aufbauen (Confidence Building).

Warum ist Testing so wichtig?

  • Kosten: Ein Fehler, der in der Produktion gefunden wird, kostet 100× mehr als einer, der beim Unit-Test entdeckt wird
  • Sicherheit: Testing verhindert Sicherheitslücken und Datenverlust
  • Wartbarkeit: Tests dokumentieren, wie Code funktionieren soll
  • Refactoring: Mit Tests können Sie Code sicher umstrukturieren
  • CI/CD: Automatisierte Tests sind Voraussetzung für Continuous Deployment
  • Kundenzufriedenheit: Weniger Bugs = glücklichere Nutzer

2. Die vier Haupt-Testarten

Software-Testing wird in verschiedene Ebenen unterteilt – von kleinen Einheiten bis zum kompletten System.

LEVEL 01

Unit-Test

Komponententest · Einzelne Funktionen

Testet einzelne Funktionen, Methoden oder Klassen isoliert. Der schnellste und häufigste Testtyp.

  • Granularität: Sehr fein (eine Funktion)
  • Geschwindigkeit: Sehr schnell (Millisekunden)
  • Isolation: Keine externen Abhängigkeiten
  • Mocks: Externe Abhängigkeiten werden simuliert
Beispiel: Testet, ob add(2, 3) wirklich 5 zurückgibt.
LEVEL 02

Integration-Test

Modultest · Zusammenspiel mehrerer Komponenten

Testet das Zusammenspiel mehrerer Komponenten oder Module. Prüft Schnittstellen und Datenflüsse.

  • Granularität: Mittel (mehrere Funktionen)
  • Geschwindigkeit: Mittel (Sekunden)
  • Abhängigkeiten: Datenbanken, APIs, Services
  • Fokus: Schnittstellen und Datenfluss
Beispiel: Testet, ob User-Service + Datenbank korrekt zusammenarbeiten.
LEVEL 03

System-Test

End-to-End · Komplettes System

Testet das komplette, integrierte System unter realen Bedingungen. Simuliert echte Nutzungsszenarien.

  • Granularität: Grob (gesamtes System)
  • Geschwindigkeit: Langsam (Minuten)
  • Umgebung: Produktionsähnlich
  • Fokus: Gesamtfunktionalität
Beispiel: Kompletter Checkout-Prozess im Online-Shop.
LEVEL 04

Acceptance-Test

Abnahmetest · Kundensicht

Testet aus Sicht des Endbenutzers oder Kunden. Validiert, ob die Anforderungen erfüllt sind.

  • Perspektive: Endbenutzer / Kunde
  • Fokus: Anforderungen (Requirements)
  • Durchführung: Oft manuell
  • Ergebnis: Go/No-Go für Release
Beispiel: Kunde testet, ob alle Features wie besprochen funktionieren.

Die Testpyramide

Die Testpyramide (von Mike Cohn) empfiehlt folgende Verteilung:

  • 70% Unit-Tests: Schnell, isoliert, viele
  • 20% Integration-Tests: Mittel, mit Abhängigkeiten
  • 10% E2E-Tests: Langsam, teuer, wenige

Grund: Unit-Tests sind schnell und günstig, E2E-Tests langsam und teuer. Je höher in der Pyramide, desto teurer und langsamer.

3. Testmethoden – Black Box, White Box, Gray Box

Je nach Wissen über die interne Struktur des Codes werden unterschiedliche Testmethoden eingesetzt.

Black-Box-Testing

Testet die Software ohne Kenntnis der internen Struktur. Nur Input und Output werden betrachtet.

  • Perspektive: Endbenutzer
  • Fokus: Funktionale Anforderungen
  • Techniken: Äquivalenzklassen, Grenzwerte
  • Vorteil: Unabhängig von Implementierung

White-Box-Testing

Testet die Software mit voller Kenntnis der internen Struktur. Prüft Code-Pfade und Logik.

  • Perspektive: Entwickler
  • Fokus: Code-Struktur, Pfade, Bedingungen
  • Techniken: Statement Coverage, Branch Coverage
  • Vorteil: Findet logische Fehler

Gray-Box-Testing

Kombination aus Black- und White-Box. Teilweise Kenntnis der internen Struktur.

  • Perspektive: Tester mit Entwicklerwissen
  • Fokus: Design, Datenstrukturen
  • Techniken: Gezielte Tests basierend auf Architektur
  • Vorteil: Effizienter als reine Black-Box

4. Das V-Modell – Teststufen im Entwicklungsprozess

Das V-Modell ist ein etablierter Entwicklungsprozess, der Testing in jede Phase integriert.

V-Modell: Entwicklung und Testing

Phase 1 Anforderungsanalyse
Phase 4 Abnahmetest (UAT)
Phase 2 Systemdesign
Phase 3 Systemtest
Phase 3 Architektur-Design
Phase 2 Integrationstest
Phase 4 Implementierung (Code)
Phase 1 Unit-Test

Kernprinzip des V-Modells

Für jede Entwicklungsphase gibt es eine entsprechende Testphase. Die Tests werden parallel zur Entwicklung geplant, aber erst nach der Implementierung ausgeführt. So wird sichergestellt, dass alle Anforderungen auch getestet werden.

5. Wichtige Testtechniken

Es gibt verschiedene Techniken, um Testfälle systematisch zu generieren:

Äquivalenzklassen

Eingaben werden in Klassen eingeteilt, die gleiches Verhalten erwarten. Ein Test pro Klasse reicht.

Grenzwertanalyse

Testet die Grenzen von Eingabebereichen (z.B. Minimum, Maximum, Minimum+1, Maximum-1).

Entscheidungstabellen

Kombinationen von Bedingungen und Aktionen werden in einer Tabelle systematisch durchgetestet.

Zustandsübergang

Testet Übergänge zwischen verschiedenen Zuständen eines Systems (z.B. Warenkorb → Checkout → Bezahlt).

Use-Case-Testing

Testet komplette Nutzungsszenarien aus Sicht des Endbenutzers (User Stories).

Exploratives Testing

Freies, kreatives Testen ohne vordefinierte Testfälle. Tester entdeckt Fehler durch Intuition.

Regression Testing

Stellt sicher, dass neue Änderungen keine bestehenden Funktionen kaputt machen.

Performance Testing

Testet Geschwindigkeit, Skalierbarkeit und Stabilität unter Last (Load Testing).

6. TDD & BDD – Moderne Test-Ansätze

Zwei moderne Ansätze, die Testing in den Entwicklungsprozess integrieren:

TDD – Test-Driven Development

Test zuerst, dann Code
  1. Red: Schreibe einen fehlschlagenden Test für die neue Funktionalität
  2. Green: Schreibe minimalen Code, um den Test bestehen zu lassen
  3. Refactor: Verbessere den Code, ohne die Tests zu brechen
  4. Repeat: Wiederhole den Zyklus für die nächste Funktionalität
// 1. Test schreiben (fehlt) assert add(2, 3) == 5 // 2. Code schreiben def add(a, b): return a + b // 3. Test läuft ✓

BDD – Behavior-Driven Development

Tests in natürlicher Sprache
  1. Define: Definiere das Verhalten in Given-When-Then-Syntax
  2. Implement: Implementiere die Funktionalität
  3. Automate: Automatisiere die Tests mit BDD-Frameworks
  4. Validate: Validiere, dass das Verhalten den Anforderungen entspricht
Feature: Login Scenario: Erfolgreicher Login Given ein registrierter User When er korrekte Daten eingibt Then wird er eingeloggt
Aspekt TDD BDD
Fokus Implementierung (Wie?) Verhalten (Was?)
Zielgruppe Entwickler Entwickler + Stakeholder
Sprache Code (JUnit, pytest) Natürliche Sprache (Gherkin)
Lesbarkeit Technisch Für alle verständlich
Tools JUnit, pytest, Jest Cucumber, SpecFlow, Behave
Vorteil Schnelles Feedback Gemeinsames Verständnis

7. Unit-Test Beispiele

Hier sehen Sie, wie Unit-Tests in verschiedenen Programmiersprachen aussehen:

Python – pytest

PYTHON # calculator.py def add(a, b): return a + b def divide(a, b): if b == 0: raise ValueError("Division by zero") return a / b # test_calculator.py import pytest from calculator import add, divide def test_add(): assert add(2, 3) == 5 assert add(-1, 1) == 0 assert add(0, 0) == 0 def test_divide(): assert divide(10, 2) == 5 assert divide(7, 2) == 3.5 with pytest.raises(ValueError): divide(1, 0)

Java – JUnit

JAVA // Calculator.java public class Calculator { public int add(int a, int b) { return a + b; } } // CalculatorTest.java import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class CalculatorTest { @Test void testAdd() { Calculator calc = new Calculator(); assertEquals(5, calc.add(2, 3)); assertEquals(0, calc.add(-1, 1)); } @Test void testAddZero() { Calculator calc = new Calculator(); assertEquals(5, calc.add(5, 0)); } }

JavaScript – Jest

JAVASCRIPT // calculator.js function add(a, b) { return a + b; } module.exports = { add }; // calculator.test.js const { add } = require('./calculator'); test('adds 2 + 3 to equal 5', () => { expect(add(2, 3)).toBe(5); }); test('adds -1 + 1 to equal 0', () => { expect(add(-1, 1)).toBe(0); }); describe('Calculator', () => { it('should handle zero', () => { expect(add(5, 0)).toBe(5); }); });

8. Testabdeckung (Code Coverage)

Die Testabdeckung misst, wie viel Prozent des Codes durch Tests abgedeckt sind.

Coverage-Level und ihre Bedeutung

Niedrig (< 50%)
30%
30%
Mittel (50-70%)
60%
60%
Gut (70-85%)
80%
80%
Ausgezeichnet (> 85%)
95%
95%

Coverage-Metriken

  • Statement Coverage: Wie viele Code-Zeilen wurden ausgeführt?
  • Branch Coverage: Wie viele Verzweigungen (if/else) wurden getestet?
  • Function Coverage: Wie viele Funktionen wurden aufgerufen?
  • Line Coverage: Wie viele Zeilen wurden durchlaufen?

Wichtig: 100% Coverage bedeutet nicht, dass der Code fehlerfrei ist! Es bedeutet nur, dass alle Zeilen ausgeführt wurden. Die Qualität der Tests ist wichtiger als die Quantität.

9. Beliebte Test-Frameworks und Tools

Für jede Programmiersprache gibt es etablierte Test-Frameworks:

JUnit
Java

Das Standard-Test-Framework für Java. Unterstützt Annotations (@Test), Assertions und Test-Suiten.

pytest
Python

Modernes, flexibles Test-Framework. Einfache Syntax, mächtige Fixtures, umfangreiche Plugin-Ökosystem.

Jest
JavaScript

Beliebtes Testing-Framework für JavaScript/TypeScript. Zero-Config, Snapshot-Testing, Mocking.

PHPUnit
PHP

Das Standard-Test-Framework für PHP. Unterstützt Assertions, Mocks, Data Providers.

Selenium
UI Testing

Automatisiert Browser-Tests. Unterstützt Chrome, Firefox, Safari. Ideal für E2E-Tests.

JMeter
Performance

Load-Testing-Tool. Simuliert tausende gleichzeitige Nutzer. Misst Performance und Skalierbarkeit.

Weitere wichtige Tools

  • Mocha / Chai: Alternatives JavaScript-Testing-Framework
  • Cypress: Modernes E2E-Testing-Framework für Web-Apps
  • Playwright: Microsofts E2E-Testing-Framework (Cross-Browser)
  • Cucumber: BDD-Framework für natürliche Sprache
  • Mockito: Mocking-Framework für Java
  • SonarQube: Code-Qualität und Testabdeckung analysieren
  • Coverage.py / Istanbul: Code-Coverage messen

10. Testing Best Practices

Test-Qualität

  • Tests sollten schnell sein (Unit-Tests < 1 Sekunde)
  • Tests sollten unabhängig voneinander sein
  • Tests sollten wiederholbar sein (deterministisch)
  • Tests sollten selbst-validierend sein (Pass/Fail)
  • Tests sollten zeitnah geschrieben werden

Automatisierung

  • Automatisiere so viele Tests wie möglich
  • Integriere Tests in CI/CD-Pipeline
  • Führe Tests bei jedem Commit aus
  • Nutze Parallelisierung für schnellere Ausführung
  • Pflege und aktualisiere Tests regelmäßig

Isolation

  • Unit-Tests sollten keine externen Abhängigkeiten haben
  • Nutze Mocks und Stubs für externe Services
  • Verwende In-Memory-Datenbanken für Tests
  • Isoliere Tests voneinander (keine Seiteneffekte)
  • Nutze Test-Fixtures für wiederholbare Testdaten

Dokumentation

  • Schreibe aussagekräftige Testnamen
  • Dokumentiere das erwartete Verhalten
  • Kommentiere komplexe Testlogik
  • Pflege Test-Dokumentation
  • Nutze Tests als lebendige Spezifikation

Die FIRST-Prinzipien für Unit-Tests

  • Fast – Tests sollten schnell laufen
  • Isolated – Tests sollten isoliert sein
  • Repeatable – Tests sollten wiederholbar sein
  • Self-Validating – Tests sollten sich selbst validieren
  • Timely – Tests sollten zeitnah geschrieben werden

11. FAQ – Häufige Fragen & Antworten

Häufige Fragen zum Software-Testing

Warum sollte ich Tests schreiben? Das kostet doch nur Zeit!

Tests sparen langfristig enorm viel Zeit:

  • Frühes Fehlerfinden: Ein Bug, der beim Testen entdeckt wird, kostet 10-100× weniger als in der Produktion
  • Refactoring-Sicherheit: Mit Tests können Sie Code sicher umstrukturieren
  • Dokumentation: Tests zeigen, wie Code funktionieren soll
  • Regressionsschutz: Neue Features brechen keine alten
  • CI/CD: Automatisierte Tests sind Voraussetzung für Deployment

Investition in Tests zahlt sich nach wenigen Wochen aus.

Was ist der Unterschied zwischen Unit- und Integration-Tests?

Unit-Tests:

  • Testen einzelne Funktionen/Klassen isoliert
  • Keine externen Abhängigkeiten (Datenbank, Netzwerk)
  • Sehr schnell (Millisekunden)
  • Verwenden Mocks für Abhängigkeiten

Integration-Tests:

  • Testen Zusammenspiel mehrerer Komponenten
  • Nutzen echte Datenbanken, APIs, Services
  • Langsamer (Sekunden bis Minuten)
  • Testen Schnittstellen und Datenfluss
Wie viel Testabdeckung (Coverage) sollte ich anstreben?

Es gibt keine magische Zahl, aber als Richtwert:

  • Minimum: 70-80% für kritischen Code
  • Gut: 80-90% für die meisten Projekte
  • Exzellent: 90%+ für sicherheitskritische Systeme

Wichtig: 100% Coverage garantiert keine Fehlerfreiheit! Die Qualität der Tests (gute Assertions, Edge Cases) ist wichtiger als die reine Prozentzahl. Konzentriere dich auf kritische Pfade und Edge Cases.

Was sind Mocks und Stubs?

Mocks und Stubs sind simulierte Objekte, die echte Abhängigkeiten ersetzen:

  • Stub: Liefert vordefinierte Antworten (z.B. "Datenbank gibt User X zurück")
  • Mock: Überprüft, ob bestimmte Methoden aufgerufen wurden
  • Fake: Funktionierende, aber vereinfachte Implementierung (z.B. In-Memory-DB)

Wann verwenden? Bei Unit-Tests, um externe Abhängigkeiten (Datenbank, API, Dateisystem) zu isolieren. Tools: Mockito (Java), unittest.mock (Python), Jest Mocks (JavaScript).

Was ist TDD und sollte ich es verwenden?

TDD (Test-Driven Development) ist ein Ansatz, bei dem Sie zuerst den Test schreiben, dann den Code, der den Test bestehen lässt:

  • Red: Test schreiben (fehlschlagend)
  • Green: Minimalen Code schreiben (Test besteht)
  • Refactor: Code verbessern

Vorteile: Besseres Design, hohe Testabdeckung, weniger Bugs. Nachteile: Steile Lernkurve, anfänglich langsamer. Empfehlung: Probieren Sie TDD für neue Features aus – es verbessert nachweislich die Code-Qualität.

Wie teste ich asynchronen Code (APIs, Promises)?

Asynchroner Code erfordert spezielle Testtechniken:

  • async/await: Moderne Test-Frameworks unterstützen async-Tests
  • Mocking: Externe APIs mit Mocks ersetzen
  • Timeouts: Tests mit Timeout absichern
  • Done-Callback: Ältere Frameworks nutzen Callbacks

Beispiel (Jest):

test('fetches user', async () => { const user = await fetchUser(1); expect(user.name).toBe('Max'); });
Was ist der Unterschied zwischen TDD und BDD?

TDD (Test-Driven Development):

  • Fokus auf Implementierung (Wie?)
  • Tests in Code (JUnit, pytest)
  • Für Entwickler
  • Technische Sprache

BDD (Behavior-Driven Development):

  • Fokus auf Verhalten (Was?)
  • Tests in natürlicher Sprache (Gherkin: Given-When-Then)
  • Für Entwickler + Stakeholder
  • Für alle verständlich

Empfehlung: TDD für Unit-Tests, BDD für Akzeptanztests und Features, die mit Nicht-Entwicklern besprochen werden.

Wie integriere ich Tests in CI/CD?

Tests in CI/CD-Pipeline integrieren:

  • Bei jedem Commit: Unit-Tests automatisch ausführen
  • Bei Pull Requests: Alle Tests + Coverage-Check
  • Vor Deployment: Integration- und E2E-Tests
  • Nightly Builds: Performance- und Lasttests

Tools: GitHub Actions, GitLab CI, Jenkins, CircleCI. Best Practice: Deployment nur, wenn alle Tests bestehen. Failing Tests blockieren den Merge.

Was ist Regression Testing und warum ist es wichtig?

Regression Testing stellt sicher, dass neue Änderungen keine bestehenden Funktionen kaputt machen:

  • Alte Tests werden bei jeder Änderung erneut ausgeführt
  • Verhindert "Das hat früher funktioniert"-Bugs
  • Essenziell für langfristige Code-Qualität

Best Practice: Automatisiere Regression-Tests und führe sie bei jedem Commit aus. Ohne Regression-Tests wird Code mit der Zeit instabil.

Welche Tests sollte ich priorisieren?

Priorisiere Tests nach Risiko und Wichtigkeit:

  • 1. Kritische Pfade: Code, der bei Fehlern große Schäden verursacht
  • 2. Komplexe Logik: Code mit vielen Bedingungen und Edge Cases
  • 3. Häufig geänderter Code: Code, der oft refactored wird
  • 4. Integrationen: Schnittstellen zu externen Systemen
  • 5. Edge Cases: Grenzfälle und Fehlerbehandlung

Weniger Priorität: Einfache Getter/Setter, UI-Layout (wenn visuell getestet).

Zusammenfassung

Die wichtigsten Punkte

  • Testing ist essenziell: Findet Fehler früh, sichert Qualität, dokumentiert Verhalten
  • 4 Testarten: Unit (isoliert), Integration (Zusammenspiel), System (E2E), Acceptance (Kunde)
  • 3 Testmethoden: Black Box (ohne Code-Kenntnis), White Box (mit), Gray Box (Mix)
  • V-Modell: Jede Entwicklungsphase hat entsprechende Testphase
  • TDD: Test zuerst, dann Code (Red-Green-Refactor)
  • BDD: Tests in natürlicher Sprache (Given-When-Then)
  • Coverage: 80%+ anstreben, Qualität > Quantität
  • Tools: JUnit (Java), pytest (Python), Jest (JS), Selenium (UI)
  • Best Practices: FIRST-Prinzipien, Isolation, Automatisierung, CI/CD-Integration
  • Regression: Alte Tests bei jeder Änderung ausführen

Testing-Tipps für den Einstieg

  • Beginne mit Unit-Tests: Einfach zu schreiben, schnelles Feedback
  • Schreibe Tests für Bug-Fixes: Verhindert, dass der Bug wiederkommt
  • Nutze ein Test-Framework: pytest, JUnit, Jest – erleichtert die Arbeit
  • Automatisiere in CI/CD: Tests bei jedem Commit ausführen
  • Lerne TDD: Verbessert nachweislich Code-Qualität und Design
  • Teste Edge Cases: Grenzfälle, leere Inputs, Fehlerbehandlung
  • Pflege Tests: Tests sind Code – regelmäßig refactoren

Weiterführende Themen

Programmiersprachen

Überblick über die wichtigsten Programmiersprachen und ihre Einsatzgebiete.

Zu Programmiersprachen
Algorithmen

Grundlegende Algorithmen, Komplexität (Big-O) und Problemlösungsstrategien.

Zu Algorithmen
Datenstrukturen

Arrays, Listen, Bäume, Graphen – wie Daten effizient organisiert werden.

Zu Datenstrukturen
Software-Engineering

Software-Entwicklungsprozesse, Modelle und Best Practices.

Zu Software-Engineering