Cover | 1 |
Titel | 3 |
Impressum | 4 |
Inhaltsverzeichnis | 5 |
Vorwort zur zweiten Auflage | 13 |
Vorwort zur ersten Auflage | 15 |
Einleitung | 17 |
Kapitel 1: Die Grundlagen des Unit Testings | 25 |
1.1 Unit Testing – Schritt für Schritt definiert | 25 |
1.1.1 Die Bedeutung guter Unit Tests | 27 |
1.1.2 Wir alle haben schon Unit Tests geschrieben (irgendwie) | 27 |
1.2 Eigenschaften eines »guten« Unit Tests | 28 |
1.3 Integrationstests | 29 |
1.3.1 Nachteile von nicht automatisierten Integrationstests im Vergleich zu automatisierten Unit Tests | 31 |
1.4 Was Unit Tests »gut« macht | 33 |
1.5 Ein einfaches Unit-Test-Beispiel | 34 |
1.6 Testgetriebene Entwicklung | 38 |
1.7 Die drei Schlüsselqualifikationen für erfolgreiches TDD | 41 |
1.8 Zusammenfassung | 41 |
Kapitel 2: Ein erster Unit Test | 43 |
2.1 Frameworks für das Unit Testing | 44 |
2.1.1 Was Unit-Testing-Frameworks bieten | 44 |
2.1.2 Die xUnit-Frameworks | 46 |
2.2 Das LogAn-Projekt wird vorgestellt | 47 |
2.3 Die ersten Schritte mit NUnit | 47 |
2.3.1 Die Installation von NUnit | 47 |
2.3.2 Das Laden der Projektmappe | 49 |
2.3.3 Die Verwendung der NUnit-Attribute in Ihrem Code | 52 |
2.4 Sie schreiben Ihren ersten Test | 53 |
2.4.1 Die Klasse Assert | 53 |
2.4.2 Sie führen Ihren ersten Test mit NUnit aus | 54 |
2.4.3 Sie fügen positive Tests hinzu | 55 |
2.4.4 Von Rot nach Grün: das erfolgreiche Ausführen der Tests | 56 |
2.4.5 Test-Code-Gestaltung | 57 |
2.5 Refactoring zu parametrisierten Tests | 57 |
2.6 Weitere NUnit-Attribute | 60 |
2.6.1 Aufbau und Abbau | 60 |
2.6.2 Auf erwartete Ausnahmen prüfen | 64 |
2.6.3 Das Ignorieren von Tests | 66 |
2.6.4 Die fließende Syntax von NUnit | 67 |
2.6.5 Das Festlegen der Testkategorien | 67 |
2.7 Das Testen auf Zustandsänderungen des Systems statt auf Rückgabewerte | 68 |
2.8 Zusammenfassung | 73 |
Kapitel 3: Die Verwendung von Stubs, um Abhängigkeiten aufzulösen | 77 |
3.1 Die Stubs werden vorgestellt | 77 |
3.2 Die Identifizierung einer Dateisystemabhängigkeit in LogAn | 78 |
3.3 Die Entscheidung, wie LogAnalyzer am einfachsten getestet werden kann | 79 |
3.4 Design-Refactoring zur Verbesserung der Testbarkeit | 82 |
3.4.1 Extrahiere ein Interface, um die dahinter liegende Implementierung durch eine andere ersetzen zu können | 83 |
3.4.2 Dependency Injection: Injiziere eine Fake-Implementierung in die zu testende Unit | 86 |
3.4.3 Injiziere einen Fake auf Konstruktor-Ebene (Construktor Injection) | 86 |
3.4.4 Simuliere Ausnahmen über Fakes | 91 |
3.4.5 Injiziere ein Fake als Property Get oder Set | 92 |
3.4.6 Injiziere einen Fake unmittelbar vor einem Methodenaufruf | 93 |
3.5 Variationen der Refactoring-Technik | 101 |
3.5.1 Die Verwendung von Extract and Override, um Fake-Resultate zu erzeugen | 101 |
3.6 Die Überwindung des Kapselungsproblems | 103 |
3.6.1 Die Verwendung von internal und [InternalsVisibleTo] | 104 |
3.6.2 Die Verwendung des Attributs [Conditional] | 104 |
3.6.3 Die Verwendung von #if und #endif zur bedingten Kompilierung | 105 |
3.7 Zusammenfassung | 106 |
Kapitel 4: Interaction Testing mit Mock-Objekten | 107 |
4.1 Wertbasiertes Testen versus zustandsbasiertes Testen versus Testen versus Interaction Testing | 107 |
4.2 Der Unterschied zwischen Mocks und Stubs | 110 |
4.3 Ein einfaches manuelles Mock-Beispiel | 111 |
4.4 Die gemeinsame Verwendung von Mock und Stub | 114 |
4.5 Ein Mock pro Test | 119 |
4.6 Fake-Ketten: Stubs, die Mocks oder andere Stubs erzeugen | 119 |
4.7 Die Probleme mit handgeschriebenen Mocks und Stubs | 121 |
4.8 Zusammenfassung | 122 |
Kapitel 5: Isolation-(Mock-Objekt-)Frameworks | 123 |
5.1 Warum überhaupt Isolation-Frameworks? | 123 |
5.2 Das dynamische Erzeugen eines Fake-Objekts | 125 |
5.2.1 Die Einführung von NSubstitute in Ihre Tests | 126 |
5.2.2 Das Ersetzen eines handgeschriebenen Fake-Objekts durch ein dynamisches | 127 |
5.3 Die Simulation von Fake-Werten | 130 |
5.3.1 Ein Mock, ein Stub und ein Ausflug in einen Test | 131 |
5.4 Das Testen auf ereignisbezogene Aktivitäten | 137 |
5.4.1 Das Testen eines Event Listeners | 137 |
5.4.2 Der Test, ob ein Event getriggert wurde | 139 |
5.5 Die aktuellen Isolation-Frameworks für .NET | 139 |
5.6 Die Vorteile und Fallstricke von Isolation-Frameworks | 141 |
5.6.1 Fallstricke, die man bei der Verwendung von Isolation-Frameworks besser vermeidet | 141 |
5.6.2 Unlesbarer Testcode | 142 |
5.6.3 Die Verifizierung der falschen Dinge | 142 |
5.6.4 Die Verwendung von mehr als einem Mock pro Test | 142 |
5.6.5 Die Überspezifizierung von Tests | 142 |
5.7 Zusammenfassung | 143 |
Kapitel 6: Wir tauchen tiefer ein in die Isolation-Frameworks | 145 |
6.1 Eingeschränkte und uneingeschränkte Frameworks | 145 |
6.1.1 Eingeschränkte Frameworks | 145 |
6.1.2 Uneingeschränkte Frameworks | 146 |
6.1.3 Wie Profiler-basierte uneingeschränkte Frameworks arbeiten | 148 |
6.2 Werte guter Isolation-Frameworks | 150 |
6.3 Eigenschaften, die Zukunftssicherheit und Benutzerfreundlichkeit unterstützen | 150 |
6.3.1 Rekursive Fakes | 151 |
6.3.2 Ignoriere Argumente als Voreinstellung | 152 |
6.3.3 Umfangreiches Fälschen | 152 |
6.3.4 Nicht striktes Verhalten von Fakes | 152 |
6.3.5 Nicht strikte Mocks | 153 |
6.4 Isolation-Framework-Design-Antimuster | 154 |
6.4.1 Konzept-Konfusion | 154 |
6.4.2 Aufnahme und Wiedergabe | 155 |
6.4.3 Klebriges Verhalten | 157 |
6.4.4 Komplexe Syntax | 157 |
6.5 Zusammenfassung | 158 |
Kapitel 7: Testhierarchie und Organisation | 161 |
7.1 Automatisierte Builds, die automatisierte Tests laufen lassen | 161 |
7.1.1 Die Anatomie eines Build-Skripts | 163 |
7.1.2 Das Anstoßen von Builds und Integration | 164 |
7.2 Testentwürfe, die auf Geschwindigkeit und Typ basieren | 165 |
7.2.1 Der menschliche Faktor beim Trennen von Unit und Integrationstests | 166 |
7.2.2 Die sichere grüne Zone | 167 |
7.3 Stellen Sie sicher, dass die Tests zu Ihrer Quellcodekontrolle gehören | 168 |
7.4 Das Abbilden der Testklassen auf den zu testenden Code | 168 |
7.4.1 Das Abbilden von Tests auf Projekte | 168 |
7.4.2 Das Abbilden von Tests auf Klassen | 169 |
7.4.3 Das Abbilden von Tests auf bestimmte Methoden | 170 |
7.5 Querschnittsbelang-Injektion | 170 |
7.6 Der Bau einer Test-API für Ihre Applikation | 173 |
7.6.1 Die Verwendung von Testklassen-Vererbungsmustern | 173 |
7.6.2 Der Entwurf von Test-Hilfsklassen und -Hilfsmethoden | 188 |
7.6.3 Machen Sie Ihre API den Entwicklern bekannt | 189 |
7.7 Zusammenfassung | 190 |
Kapitel 8: Die Säulen guter Unit Tests | 191 |
8.1 Das Schreiben vertrauenswürdiger Tests | 191 |
8.1.1 Die Entscheidung, wann Tests entfernt oder geändert werden | 192 |
8.1.2 Vermeiden Sie Logik in Tests | 197 |
8.1.3 Testen Sie nur einen Belang | 199 |
8.1.4 Trennen Sie Unit Tests von Integrationstests | 200 |
8.1.5 Stellen Sie Code-Reviews mit Codeabdeckung sicher | 200 |
8.2 Das Schreiben wartbarer Tests | 202 |
8.2.1 Das Testen privater oder geschützter Methoden | 202 |
8.2.2 Das Entfernen von Duplizitäten | 204 |
8.2.3 Die Verwendung von Setup-Methoden in einer wartbaren Art und Weise | 208 |
8.2.4 Das Erzwingen der Test-Isolierung | 211 |
8.2.5 Vermeiden Sie mehrfache Asserts für unterschiedliche Belange | 217 |
8.2.6 Der Vergleich von Objekten | 219 |
8.2.7 Vermeiden Sie eine Überspezifizierung der Tests | 222 |
8.3 Das Schreiben lesbarer Tests | 224 |
8.3.1 Die Benennung der Unit Tests | 225 |
8.3.2 Die Benennung der Variablen | 226 |
8.3.3 Benachrichtigen Sie sinnvoll | 227 |
8.3.4 Das Trennen der Asserts von den Aktionen | 228 |
8.3.5 Aufbauen und Abreißen | 229 |
8.4 Zusammenfassung | 229 |
Kapitel 9: Die Integration von Unit Tests in die Organisation | 233 |
9.1 Schritte, um ein Agent des Wandels zu werden | 233 |
9.1.1 Seien Sie auf die schweren Fragen vorbereitet | 234 |
9.1.2 Überzeugen Sie Insider: Champions und Blockierer | 234 |
9.1.3 Identifizieren Sie mögliche Einstiegspunkte | 235 |
9.2 Wege zum Erfolg | 237 |
9.2.1 Guerilla-Implementierung (Bottom-up) | 237 |
9.2.2 Überzeugen Sie das Management (Top-down) | 237 |
9.2.3 Holen Sie einen externen Champion | 238 |
9.2.4 Machen Sie Fortschritte sichtbar | 238 |
9.2.5 Streben Sie bestimmte Ziele an | 240 |
9.2.6 Machen Sie sich klar, dass es Hürden geben wird | 241 |
9.3 Wege zum Misserfolg | 242 |
9.3.1 Mangelnde Triebkraft | 242 |
9.3.2 Mangelnde politische Unterstützung | 242 |
9.3.3 Schlechte Implementierungen und erste Eindrücke | 242 |
9.3.4 Mangelnde Teamunterstützung | 243 |
9.4 Einflussfaktoren | 243 |
9.5 Schwierige Fragen und Antworten | 245 |
9.5.1 Wie viel zusätzliche Zeit wird der aktuelle Prozess für das Unit Testing benötigen? | 245 |
9.5.2 Ist mein Job bei der QS in Gefahr wegen des Unit Testing? | 247 |
9.5.3 Woher wissen wir, dass Unit Tests wirklich funktionieren? | 247 |
9.5.4 Gibt es denn einen Beweis, dass Unit Testing hilft? | 248 |
9.5.5 Warum findet die QS immer noch Bugs? | 248 |
9.5.6 Wir haben eine Menge Code ohne Tests: Wo fangen wir an? | 249 |
9.5.7 Wir arbeiten mit mehreren Sprachen: Ist Unit Testing da praktikabel? | 249 |
9.5.8 Was ist, wenn wir eine Kombination aus Soft- und Hardware entwickeln? | 250 |
9.5.9 Wie können wir wissen, dass wir keine Bugs in unseren Tests haben? | 250 |
9.5.10 Mein Debugger zeigt mir, dass mein Code funktioniert: Wozu brauche ich Tests? | 250 |
9.5.11 Müssen wir Code im TDD-Stil schreiben? | 250 |
9.6 Zusammenfassung | 251 |
Kapitel 10: Der Umgang mit Legacy-Code | 253 |
10.1 Wo soll man mit dem Einbauen der Tests beginnen? | 254 |
10.2 Bestimmen Sie eine Auswahlstrategie | 256 |
10.2.1 Vor- und Nachteile der Strategie »Einfaches zuerst« | 256 |
10.2.2 Vor- und Nachteile der Strategie »Schwieriges zuerst« | 256 |
10.3 Schreiben Sie Integrationstests, bevor Sie mit dem Refactoring beginnen | 257 |
10.4 Wichtige Tools für das Unit Testing von Legacy-Code | 258 |
10.4.1 Abhängigkeiten isolieren Sie leicht mit uneingeschränkten Isolation-Frameworks | 259 |
10.4.2 Verwenden Sie JMockit für Java-Legacy-Code | 260 |
10.4.3 Verwenden Sie Vise beim Refactoring Ihres Java-Codes | 262 |
10.4.4 Verwenden Sie Akzeptanztests, bevor Sie mit dem Refactoring beginnen | 263 |
10.4.5 Lesen Sie das Buch von Michael Feathers zu Legacy-Code | 264 |
10.4.6 Verwenden Sie NDepend, um Ihren Produktionscode zu untersuchen | 265 |
10.4.7 Verwenden Sie ReSharper für die Navigation und das Refactoring des Produktionscodes | 265 |
10.4.8 Spüren Sie Code-Duplikate (und Bugs) mit Simian und TeamCity auf | 265 |
10.5 Zusammenfassung | 266 |
Kapitel 11: Design und Testbarkeit | 267 |
11.1 Warum sollte ich mir Gedanken um die Testbarkeit in meinem Design machen? | 267 |
11.2 Designziele für die Testbarkeit | 268 |
11.2.1 Deklarieren Sie Methoden standardmäßig als virtuell | 269 |
11.2.2 Benutzen Sie ein Interface-basiertes Design | 270 |
11.2.3 Deklarieren Sie Klassen standardmäßig als nicht versiegelt | 270 |
11.2.4 Vermeiden Sie es, konkrete Klassen innerhalb von Methoden mit Logik zu instanziieren | 270 |
11.2.5 Vermeiden Sie direkte Aufrufe von statischen Methoden | 271 |
11.2.6 Vermeiden Sie Konstruktoren und statische Konstruktoren, die Logik enthalten | 271 |
11.2.7 Trennen Sie die Singleton-Logik und Singleton-Halter | 272 |
11.3 Vor- und Nachteile des Designs zum Zwecke der Testbarkeit | 273 |
11.3.1 Arbeitsumfang | 274 |
11.3.2 Komplexität | 274 |
11.3.3 Das Preisgeben von sensiblem IP | 274 |
11.3.4 Manchmal geht’s nicht | 275 |
11.4 Alternativen des Designs zum Zwecke der Testbarkeit | 275 |
11.4.1 Design-Argumente und Sprachen mit dynamischen Typen | 275 |
11.5 Beispiel eines schwer zu testenden Designs | 277 |
11.6 Zusammenfassung | 281 |
11.7 Zusätzliche Ressourcen | 282 |
Anhang A: Tools und Frameworks | 285 |
A.1 Isolation-Frameworks | 285 |
A.1.1 Moq | 286 |
A.1.2 Rhino Mocks | 286 |
A.1.3 Typemock Isolator | 287 |
A.1.4 JustMock | 287 |
A.1.5 Microsoft Fakes (Moles) | 287 |
A.1.6 NSubstitute | 288 |
A.1.7 FakeItEasy | 288 |
A.1.8 Foq | 289 |
A.1.9 Isolator++ | 289 |
A.2 Test-Frameworks | 289 |
A.2.1 Mighty Moose (auch bekannt als ContinuousTests) Continuous Runner | 290 |
A.2.2 NCrunch Continuous Runner | 290 |
A.2.3 Typemock Isolator Test Runner | 290 |
A.2.4 CodeRush Test Runner | 290 |
A.2.5 ReSharper Test Runner | 291 |
A.2.6 TestDriven.NET Runner | 291 |
A.2.7 NUnit GUI Runner | 292 |
A.2.8 MSTest Runner | 292 |
A.2.9 Pex | 292 |
A.3 Test-APIs | 293 |
A.3.1 MSTest-API – Microsofts Unit-Testing-Framework | 293 |
A.3.2 MSTest für Metro Apps (Windows Store) | 293 |
A.3.3 NUnit API | 294 |
A.3.4 xUnit.net | 294 |
A.3.5 Fluent Assertions Helper API | 294 |
A.3.6 Shouldly Helper API | 294 |
A.3.7 SharpTestsEx Helper API | 295 |
A.3.8 AutoFixture Helper API | 295 |
A.4 IoC-Container | 295 |
A.4.1 Autofac | 296 |
A.4.2 Ninject | 297 |
A.4.3 Castle Windsor | 297 |
A.4.4 Microsoft Unity | 297 |
A.4.5 StructureMap | 297 |
A.4.6 Microsoft Managed Extensibility Framework | 297 |
A.5 Datenbanktests | 298 |
A.5.1 Verwenden Sie Integrationstests für Ihre Datenschicht | 298 |
A.5.2 Verwenden Sie TransactionScope für ein Rollback der Daten | 298 |
A.6 Webtests | 299 |
A.6.1 Ivonna | 300 |
A.6.2 Team System Web Test | 300 |
A.6.3 Watir | 300 |
A.6.4 Selenium WebDriver | 300 |
A.6.5 Coypu | 301 |
A.6.6 Capybara | 301 |
A.6.7 JavaScript-Tests | 301 |
A.7 UI-Tests (Desktop) | 301 |
A.8 Thread-bezogene Tests | 302 |
A.8.1 Microsoft CHESS | 302 |
A.8.2 Osherove.ThreadTester | 302 |
A.9 Akzeptanztests | 302 |
A.9.1 FitNesse | 303 |
A.9.2 SpecFlow | 303 |
A.9.3 Cucumber | 303 |
A.9.4 TickSpec | 304 |
A.10 API-Frameworks im BDD-Stil | 304 |
Stichwortverzeichnis | 305 |