🧪 Unit Testing
Unit Tests sind kein Selbstzweck.
Sie sind ein zentrales Qualitätsinstrument zur Reduktion technischer Risiken und zur Absicherung von Veränderbarkeit.
Professionelle Softwareentwicklung ist ohne systematische Tests langfristig nicht skalierbar.
1. Ziel und Einordnung
1.1 Was sind Unit Tests?
Unit Tests prüfen eine isolierte fachliche Einheit (Funktion, Methode, Klasse) unter kontrollierten Bedingungen.
Charakteristika:
- Keine echte Infrastruktur
- Keine echte Datenbank
- Keine echten HTTP-Calls
- Keine Framework-Initialisierung
- Vollständig deterministisch
Eine Unit ist dabei nicht zwingend eine Klasse, sondern eine fachlich geschlossene Logikeinheit.
1.2 Wofür sind Unit Tests da?
Unit Tests dienen der:
- Absicherung fachlicher Logik
- Dokumentation von Verhalten
- Prävention von Regressionen
- Ermöglichung sicherer Refactorings
- Rückmeldung über Testbarkeit (Design-Feedback)
Gut testbarer Code ist in der Regel besser geschnitten, entkoppelt und wartbar.
1.3 Was sind Unit Tests nicht?
Unit Tests sind:
- kein Ersatz für Integrations- oder E2E-Tests
- kein Beweis für Fehlerfreiheit
- kein KPI-Instrument zur Erreichung von 100 % Coverage
- keine Simulation realer Produktionsumgebungen
Sie prüfen Verhalten isoliert – nicht Systemintegration.
2. Test-Mindset
2.1 Qualität vor Coverage
100 % Line Coverage bedeutet nicht 100 % Qualität.
Ziel ist nicht maximale Coverage, sondern:
- Vertrauen in kritische Logik
- Reduktion fachlicher Risiken
- Absicherung gegen unbeabsichtigte Änderungen
Branch Coverage ist aussagekräftiger als reine Line Coverage.
Kritische Domänenlogik sollte besonders sorgfältig getestet werden.
2.2 Teste Verhalten, nicht Implementierung
Tests beschreiben beobachtbares Verhalten – nicht interne Details.
Nicht testen:
- private Methoden
- interne Hilfsfunktionen
- konkrete Framework-Mechaniken
Ein Refactoring darf Tests nicht brechen, solange sich das Verhalten nicht ändert.
2.3 Ein Test = ein klarer Grund zu scheitern
Ein Test sollte:
- einen klar abgegrenzten Aspekt prüfen
- bei Fehlern eindeutig diagnostisch sein
Mehrere Assertions sind erlaubt, wenn sie denselben Sachverhalt betreffen.
3. Test-Design-Prinzipien
3.1 AAA-Prinzip
- Arrange
- Act
- Assert
Klare Struktur erhöht Lesbarkeit und Wartbarkeit.
3.2 Given–When–Then
Tests sind auch Dokumentation.
Beschreibe Verhalten aus fachlicher Perspektive, nicht aus technischer Sicht.
3.3 Keine Logik im Test
Tests sind keine zweite Implementierung.
Vermeiden:
- komplexe Berechnungen
- Schleifen
- Verzweigungen
- eigene Entscheidungslogik
Wenn der Test zu komplex wird, ist meist auch die getestete Einheit zu komplex.
3.4 Determinismus
Unit Tests müssen reproduzierbar sein.
Nicht erlaubt:
- Zufallswerte
- echte Zeitabhängigkeit
- echte Netzwerke
- globale Zustände
Zeit, Zufall und Infrastruktur müssen abstrahiert werden.
4. Was sollte getestet werden?
4.1 Domänenlogik (höchste Priorität)
- Berechnungen
- Validierungsregeln
- Entscheidungslogik
- Zustandsübergänge
4.2 Randfälle (Edge Cases)
null/undefined- Grenzwerte
- negative Werte
- leere Collections
- Sonderfälle laut Fachkonzept
4.3 Fehlerpfade
- Exceptions
- Guard Clauses
- Fehlerrückgaben
- Invalid States
Happy Path alleine reicht nicht aus.
5. Was sollte nicht getestet werden?
- Framework-interne Mechanik
- Getter/Setter ohne Logik
- reine DTOs ohne Verhalten
- fremde Bibliotheken
- Template-Bindings ohne Logik
Nicht jede Codezeile verdient einen Test.
6. Mocks und Test Doubles
6.1 Wann mocken?
Mocks sind zulässig für:
- externe Abhängigkeiten
- Infrastruktur
- HTTP
- Datenbanken
- Messaging
6.2 Wann nicht mocken?
Nicht mocken:
- reine Domänenlogik
- Value Objects
- deterministische Berechnungen
6.3 Over-Mocking vermeiden
Zu viele Mocks führen zu:
- fragilen Tests
- starker Kopplung an Implementierungsdetails
- Scheinsicherheit
Wenn ein Test bei jeder internen Umstrukturierung bricht, ist er falsch geschnitten.
7. Coverage-Arten
Line Coverage
Wurde Code ausgeführt?
Branch Coverage
Wurden alle Verzweigungen getestet?
Condition Coverage
Wurden boolesche Bedingungen vollständig evaluiert?
Mutation Testing (fortgeschritten)
Erkennen Tests echte Logikfehler?
Empfehlung:
- Branch Coverage > Line Coverage
- Kritische Kernlogik priorisieren
- Coverage als Indikator, nicht als Ziel
8. Testbarkeit als Architekturindikator
Testbarkeit ist ein Qualitätsmerkmal der Architektur.
Gut testbarer Code zeichnet sich aus durch:
- klare Verantwortlichkeiten (SRP)
- geringe Kopplung
- Dependency Injection
- entkoppelte Side Effects
- framework-unabhängigen Domain Layer
Wenn etwas schwer testbar ist, ist es meist schlecht geschnitten.
9. Naming und Lesbarkeit
describe definiert Kontext.
it beschreibt Verhalten.
Beispiele:
returns false if amount is negativethrows when id is undefined
Vermeiden:
- „should call method“
- Implementierungsdetails im Namen
10. Typische Anti-Patterns
- Tests auf private Methoden
- sehr große Testfälle
- viele lose Assertions ohne Zusammenhang
- Snapshot-Tests für Logik
- flakey Tests
- Bedingungslogik im Test
- Tests, die nur für Coverage existieren
11. Unit Tests im Kontext von AI
AI kann Code generieren, aber kein Domänenverständnis garantieren.
Risiken ohne Tests:
- verdeckte Architekturverstöße
- fragile Implementierungen
- falsch verstandene Business-Regeln
Unit Tests sichern:
- Domänenwissen
- Konsistenz
- langfristige Wartbarkeit
AI erhöht Produktivität – Tests sichern Qualität.
12. Definition of Done
Ein Feature gilt nicht als fertig, wenn:
- keine Unit Tests existieren
- nur der Happy Path getestet wurde
- Fehlerfälle fehlen
- Randfälle ignoriert wurden
- Tests nur formell, aber nicht fachlich sinnvoll sind
Kernaussage
Unit Tests sind kein optionales Tool.
Sie sind ein Ausdruck professioneller Engineering-Kultur.
In komplexen Systemen mit wachsender Codebasis, heterogenen Teams und AI-unterstützter Entwicklung sichern sie nicht nur Codequalität –
sie sichern langfristige Veränderbarkeit.