1. Prinzipien
1.1 Interface Segregation Principle
Warum? Leistungsbeschreibungen, die unabhängig von einer konkreten Erfüllung sind, machen unabhängig.
- Interfaces sollten möglichst klein sein, um unnötige Kopplung zu vermeiden.
- Interfaces sollten nur Dinge enthalten, die wirklich eng zusammen gehören (hohe Kohäsion).
- Ziel: möglichst geringe Kopplung ziwschen den Komponenten
Warum? Punktgenaues Testen setzt Isolation von Klassen voraus. Isolation entsteht, wenn Klassen keine Abhängigkeiten von Implementationen mehr enthalten – weder zur Laufzeit, noch zur Übersetzungszeit. Konkrete Abhängigkeiten sollten deshalb so spät wie möglich entschieden werden. Am besten zur Laufzeit.
- High-Level Klassen sollen nicht von Low-Level Klassen abhängig sein, sondern beide von Interfaces.
- Interfaces sollen nicht von Details abhängig sein, sondern Details von Interfaces.
- Mindestanforderung im 3. Grad: Abhängigkeiten über Konstruktoren injizieren
Warum? Wer mit Erben zu tun hat, möchte keine Überraschungen erleben, wenn er mit Erblassern vertraut ist.
- Kernaussage: Subtypen müssen sich so verhalten wie ihr Basistyp
- allgemeiner: ein Subtyp darf die Funktionalität eines Basistyps lediglich erweitern, aber nicht einschänken
- Empfehlung: über Vererbung genau nachdenken
- siehe Favor Composition over Inheritance (FCoI), roter Grad
- bei Vererbung über Verhalten nachdenken, nicht nur über Struktur
Warum? Wenn sich eine Komponente überraschenderweise anders verhält als erwartet, wird ihre Anwendung unnötig kompliziert und fehleranfällig.
- Software sollte überraschungsarm implementiert sein. Jede Überraschung stellt eine Unterbrechung dar und stört den kreativen Prozess der Softwareentwicklung.
- Die testgetriebene Entwicklung fördert überraschungsarme Schnittstellen.
Warum? Durch das Verbergen von Details in einer Schnittstelle werden die Abhängigkeiten reduziert.
- Je mehr Details von außen sichtbar sind, desto höher ist die Kopplung zwischen der Klasse und ihren Verwendern.
- Benutzen die Verwender einer Klasse erstmal ein Detail, wird es schwerer, dieses Detail zu verändern.
2.1 Automatisierte Unit-Tests
Warum? Nur automatisierte Tests werden auch wirklich konsequent ausgeführt. Je punktgenauer sie Code testen, desto besser.
- Regressionstests, um Korrektheit von Änderungen sicherzustellen und Angst vor Änderungen zu nehmen
- Automatisierung notwendig, da händisch nicht praktikabel
- Automatisierte Tests sparen Zeit und nehmen Angst.
Warum? Ohne Attrappen keine einfach kontrollierbaren Tests.
- Will man eine Komponente isoliert testen, müssen die Abhängigkeiten zu anderen Komponenten abgetrennt werden.
- Beim Isolieren werden sogenannte Mockups anstelle der echten Komponenten verwendet.
- andere Bezeichnungen für Attrappen: Stub, Dummy, Fake (teilweise mit unterschiedlichen Funktionsweisen)
Warum? Traue nur Tests, von denen du weißt, dass sie auch wirklich das Testareal abdecken.
- Unit Tests sollten nach Möglichkeit alle Pfade durch unseren Code abdecken.
- Die Code Coverage Analyse dient dazu, Bereiche im Code aufzudecken, die noch nicht während der automatisierten Tests ausgeführt werden.
- Mögliche Metriken:
- C0-Überdeckung = Anweisungsüberdeckung
- C1-Überdeckung = Entscheidungs- / Zweigüberdeckung
- Ziel: theoretisch 100% Überdeckung, praktisch mehr als 90% Überdeckung
Warum? Am besten lernen wir von anderen und in Gemeinschaft.
- Gedankenaustausch, Diskussionen, Erfahrungen austauschen, "über den Tellerrand blicken"
- z.B. regionale User Groups, überregionale Entwicklerkonferenzen
Warum? Es ist nicht möglich, Code direkt in der ultimativen Form zu schreiben.
- Erweiterung zum roten Grad
- unbedingte Voraussetzung: automatisierte Tests