Inhalt | 6 |
Geleitwort | 16 |
Vorwort | 18 |
Teil I Grundlagen | 28 |
1 Softwarequalität | 30 |
1.1 Was ist Softwarequalität? | 30 |
1.2 Externe Qualität | 31 |
1.3 Interne Qualität | 31 |
1.4 Technische Schulden | 32 |
1.5 Konstruktive Qualitätssicherung | 34 |
1.6 Sauberer Code | 35 |
1.6.1 Explizite und minimale Abhängigkeiten | 36 |
1.6.2 Klare Verantwortlichkeiten | 36 |
1.6.3 Keine Duplikation | 36 |
1.6.4 Kurze Methoden mit wenigen Ausführungszweigen | 36 |
1.7 Software-Metriken | 36 |
1.7.1 Zyklomatische Komplexität und NPath-Komplexität | 37 |
1.7.2 Change Risk Anti-Patterns (CRAP) Index | 37 |
1.7.3 Non-Mockable Total Recursive Cyclomatic Complexity | 38 |
1.7.4 Global Mutable State | 38 |
1.7.5 Kohäsion und Kopplung | 39 |
1.8 Werkzeuge | 39 |
1.9 Fazit | 41 |
2 Testen von Software | 44 |
2.1 Einführung | 44 |
2.2 Systemtests | 46 |
2.2.1 Testen im Browser | 46 |
2.2.2 Automatisierte Tests | 47 |
2.2.3 Testisolation | 49 |
2.2.4 Akzeptanztests | 50 |
2.2.5 Grenzen von Systemtests | 50 |
2.3 Unit-Tests | 51 |
2.3.1 Rückgabewerte | 53 |
2.3.2 Abhängigkeiten | 55 |
2.3.3 Seiteneffekte | 56 |
2.3.4 Stub- und Mock-Objekte | 56 |
2.4 Die Softwaretestpyramide | 58 |
2.5 Integrationstests | 63 |
2.5.1 Kollaborierende Systeme ersetzen | 64 |
2.5.2 Datenbanken ersetzen | 65 |
2.5.3 Die GUI (zunächst) ignorieren | 65 |
2.5.4 Frontend-Tests | 66 |
2.5.5 PHPUnit als Infrastruktur | 66 |
2.5.6 Realisierung | 67 |
2.6 Fazit | 79 |
3 Testen von Legacy Code | 80 |
3.1 Einführung | 80 |
3.2 Praxisbeispiel | 82 |
3.2.1 Vorbereitungen | 85 |
3.2.2 Globale Abhängigkeiten | 89 |
3.2.3 Datenquellen | 90 |
3.2.4 Asynchrone Vorgänge | 96 |
3.2.5 Änderungen in der Datenbank | 101 |
3.2.6 Nicht vorhersagbare Ergebnisse | 102 |
3.2.7 Eingabedaten | 105 |
3.2.8 Weiterführende Überlegungen | 106 |
3.3 Fazit | 107 |
Teil II Fortgeschrittene Themen | 108 |
4 Bad Practices in Unit-Tests | 110 |
4.1 Einführung | 110 |
4.2 Warum guter Testcode wichtig ist | 110 |
4.3 Bad Practices und Test-Smells | 111 |
4.3.1 Duplizierter Testcode | 112 |
4.3.2 Zusicherungsroulette und begierige Tests | 113 |
4.3.3 Fragile Tests | 116 |
4.3.4 Obskure Tests | 118 |
4.3.5 Lügende Tests | 124 |
4.3.6 Langsame Tests | 125 |
4.3.7 Konditionale Logik in Tests | 127 |
4.3.8 Selbstvalidierende Tests | 128 |
4.3.9 Websurfende Tests | 129 |
4.3.10 Mock-Overkill | 130 |
4.3.11 Skip-Epidemie | 132 |
4.4 Fazit | 132 |
5 Kontinuierliche Integration | 134 |
5.1 Einführung | 134 |
5.1.1 Kontinuierliche Integration | 135 |
5.1.2 Statische Analyse | 138 |
5.2 Installation und Inbetriebnahme | 150 |
5.3 Konfiguration | 150 |
5.3.1 Statische Tests | 152 |
5.3.2 Dynamische Tests | 158 |
5.3.3 Reporting | 159 |
5.3.4 Deliverables erzeugen | 160 |
5.4 Betrieb | 162 |
5.5 Weiterführende Themen | 162 |
5.5.1 Continuous Deployment | 162 |
5.5.2 Einen Reverse Proxy nutzen | 164 |
5.5.3 Kontinuierliche Integration und agile Paradigmen | 164 |
5.6 Fazit | 165 |
6 Testen von Datenbank-Interaktionen | 168 |
6.1 Einführung | 168 |
6.2 Pro und Kontra | 169 |
6.2.1 Was gegen Datenbanktests spricht | 169 |
6.2.2 Warum wir Datenbanktests schreiben sollten | 170 |
6.3 Was wir testen sollten | 171 |
6.4 Datenbanktests schreiben | 172 |
6.4.1 Die Datenbankverbindung mocken | 172 |
6.4.2 Die Datenbankerweiterung von PHPUnit | 173 |
6.4.3 Die Klasse für Datenbanktestfälle | 174 |
6.4.4 Die Verbindung zur Testdatenbank aufbauen | 175 |
6.4.5 Datenbestände erzeugen | 178 |
6.4.6 Operationen auf den Daten | 193 |
6.4.7 Tests schreiben | 196 |
6.4.8 Den Datenbanktester benutzen | 203 |
6.5 Testgetriebene Entwicklung und Datenbanktests | 206 |
6.6 Datenbanktests als Regressionstests | 206 |
6.6.1 Probleme mit den Daten testen | 207 |
6.6.2 Probleme testen, die durch Daten sichtbar werden | 208 |
6.7 Fazit | 209 |
7 Gebrauchstauglichkeit | 210 |
7.1 Einführung | 210 |
7.2 Anything goes – aber zu welchem Preis? | 212 |
7.3 Designaspekte | 213 |
7.3.1 Barrierefreiheit | 213 |
7.3.2 Lesbarkeit | 214 |
7.3.3 Label für Formularelemente | 215 |
7.3.4 Tastaturbedienbare Webseite | 215 |
7.3.5 Gute Farbkontraste | 216 |
7.3.6 Logo zur Startseite verlinken | 217 |
7.3.7 Alternativtexte für Bilder | 217 |
7.3.8 Hintergrundbild mit Hintergrundfarbe | 217 |
7.3.9 Druckversion nicht vergessen | 217 |
7.3.10 Erkennbare Links | 217 |
7.3.11 Gute Bookmarks | 218 |
7.3.12 Keine Frames | 218 |
7.3.13 Skalierbare Schrift | 218 |
7.4 Technische Aspekte | 219 |
7.4.1 Performanz | 219 |
7.4.2 JavaScript | 221 |
7.5 Benutzerführung | 222 |
7.5.1 Der Mythos des Falzes | 222 |
7.5.2 Feedback bei Interaktionen | 223 |
7.5.3 Navigation | 223 |
7.5.4 Popups und andere Störenfriede | 224 |
7.5.5 Gewohnheiten bedienen, Erwartungen nicht enttäuschen | 225 |
7.5.6 Fehlertoleranz und Feedback | 226 |
7.6 Testen der Usability | 226 |
7.7 Fazit | 227 |
8 Performanz | 230 |
8.1 Einführung | 230 |
8.1.1 Werkzeuge | 231 |
8.1.2 Umgebungsbezogene Gesichtspunkte | 232 |
8.2 Lasttests | 233 |
8.2.1 Apache Bench | 234 |
8.2.2 Pylot | 236 |
8.2.3 Weitere Werkzeuge für Lasttests | 238 |
8.3 Profiling | 239 |
8.3.1 Callgrind | 240 |
8.3.2 APD | 244 |
8.3.3 Xdebug | 246 |
8.3.4 XHProf | 246 |
8.3.5 OProfile | 249 |
8.4 Systemmetriken | 250 |
8.4.1 strace | 250 |
8.4.2 Sysstat | 251 |
8.4.3 Lösungen im Eigenbau | 253 |
8.5 Übliche Fallstricke | 254 |
8.5.1 Entwicklungsumgebung gegen Produktivumgebung | 254 |
8.5.2 CPU-Zeit | 254 |
8.5.3 Mikro-Optimierungen | 255 |
8.5.4 PHP als Glue Language | 255 |
8.5.5 Priorisierung von Optimierungen | 256 |
8.6 Fazit | 257 |
9 Sicherheit | 258 |
9.1 Was ist eigentlich Sicherheit? | 258 |
9.2 Secure by Design | 259 |
9.2.1 Der Betrieb | 259 |
9.2.2 Physikalischer Zugang | 260 |
9.2.3 Software-Entwicklung | 261 |
9.3 Was kostet Sicherheit? | 264 |
9.4 Die häufigsten Probleme | 265 |
9.5 Fazit | 274 |
10 Testbasierte Entwicklung verkaufen | 276 |
10.1 Vom prozeduralen Code zum testbasierten Vorgehen | 276 |
10.2 Ziele der testbasierten Entwicklung | 278 |
10.3 Aufwände für Software-Entwicklung | 279 |
10.4 Möglichst wenige technische Schulden aufnehmen! | 281 |
10.5 Offenlegung von Risiken mit ATAM | 282 |
10.5.1 Diskutieren und entscheiden | 285 |
10.5.2 Mit ATAM transparente Entscheidungen herbeiführen | 285 |
10.6 Kalkulation testbasierter Entwicklung | 285 |
10.6.1 Risiken als Argumentationshilfe berechnen | 286 |
10.6.2 Langsamere Entwicklung bei höherer Qualität | 286 |
10.6.3 Automatisierungs- und Abdeckungsgrad durch Tests bestimmen | 288 |
10.7 Strategische Argumente für die Einführung testbasierter Entwicklung | 289 |
10.7.1 Qualität und Nachhaltigkeit als Teil des Leistungsversprechens | 289 |
10.7.2 Initiale Mehraufwände, die sich für den Auftraggeber lohnen | 290 |
10.8 Das Angebot richtig verhandeln | 291 |
10.9 Formulierung des Angebots | 295 |
10.9.1 Inhalte des Angebots | 296 |
10.9.2 Ein Angebot ohne Verhandlung abgeben? | 296 |
10.10 Fazit | 297 |
Teil III Fallstudien: Open-Source | 298 |
11 TYPO3: die agile Zukunft eines schwergewichtigen Projekts | 300 |
11.1 Einführung | 300 |
11.1.1 Die Geschichte von TYPO3 – 13 Jahre in 13 Absätzen | 300 |
11.1.2 Den Neuanfang wagen! | 302 |
11.1.3 Unsere Erfahrungen mit dem Testen | 303 |
11.2 Grundsätze und Techniken | 304 |
11.2.1 Bittersüße Elefantenstückchen | 304 |
11.2.2 Testgetriebene Entwicklung | 305 |
11.2.3 Tests als Dokumentation | 306 |
11.2.4 Kontinuierliche Integration | 307 |
11.2.5 Sauberer Code | 308 |
11.2.6 Refaktorierung | 309 |
11.2.7 Programmierrichtlinien | 310 |
11.2.8 Domänengetriebenes Design | 312 |
11.3 Vorgehen bei der Entwicklung | 312 |
11.3.1 Neuen Code entwickeln | 313 |
11.3.2 Code erweitern und ändern | 313 |
11.3.3 Code optimieren | 314 |
11.3.4 Fehler finden und beheben | 316 |
11.3.5 Alten Code fachgerecht entsorgen | 316 |
11.4 Testrezepte | 317 |
11.4.1 Ungewollt funktionale Unit-Tests | 317 |
11.4.2 Zugriffe auf das Dateisystem | 318 |
11.4.3 Konstruktoren in Interfaces | 319 |
11.4.4 Abstrakte Klassen testen | 320 |
11.4.5 Testen von geschützten Methoden | 320 |
11.4.6 Verwendung von Callbacks | 322 |
11.5 Auf in die Zukunft | 324 |
12 Testen von Symfony und Symfony-Projekten | 326 |
12.1 Einführung | 326 |
12.2 Ein Framework testen | 327 |
12.2.1 Der Release-Management-Prozess von Symfony | 327 |
12.2.2 Verhältnis von Testcode und getestetem Code | 329 |
12.2.3 Die Ausführung der Testsuite muss schnell sein | 329 |
12.2.4 Gesammelte Erfahrungen | 330 |
12.3 Testen von Webanwendungen | 335 |
12.3.1 Die Hemmschwelle für das Testen abbauen | 335 |
12.3.2 Unit-Tests | 336 |
12.3.3 Funktionale Tests | 341 |
12.4 Fazit | 345 |
13 Testen von Grafikausgaben | 346 |
13.1 Einführung | 346 |
13.2 Entwicklungsphilosophie | 347 |
13.3 Die ezcGraph-Komponente | 347 |
13.3.1 Architektur | 349 |
13.4 Ausgabetreiber durch Mock-Objekt ersetzen | 1 |
13.4.1 Mehrfache Erwartungen | 352 |
13.4.2 Structs | 353 |
13.4.3 Generierung der Erwartungen | 354 |
13.4.4 Zusammenfassung | 355 |
13.5 Binäre Ausgaben testen | 355 |
13.5.1 Die Ausgabetreiber | 356 |
13.5.2 Generierung der Erwartungen | 356 |
13.5.3 SVG | 357 |
13.5.4 Bitmap-Erzeugung | 358 |
13.5.5 Flash | 361 |
13.6 Fazit | 364 |
14 Testen von serviceorientierten APIs | 366 |
14.1 Die Probleme | 368 |
14.2 API-Zugangskennungen | 369 |
14.3 API-Beschränkungen | 372 |
14.4 Service-Protokolle offline testen | 373 |
14.5 Konkrete Services offline testen | 378 |
14.6 Fazit | 383 |
15 Wie man einen WebDAV-Server testet | 384 |
15.1 Über die eZ WebDAV-Komponente | 384 |
15.1.1 WebDAV | 384 |
15.1.2 Architektur | 386 |
15.2 Herausforderungen bei der Entwicklung | 388 |
15.2.1 Anforderungsanalyse | 388 |
15.2.2 TDD nach RFC | 389 |
15.2.3 Den Server testen | 390 |
15.3 Automatisierte Akzeptanztests mit PHPUnit | 392 |
15.3.1 Test-Trails aufzeichnen | 394 |
15.3.2 Das Testrezept | 395 |
15.3.3 Integration mit PHPUnit | 396 |
15.4 Fazit | 405 |
Teil IV Fallstudien: Unternehmen | 406 |
16 swoodoo – eine wahrhaft agile Geschichte | 408 |
16.1 Einführung | 408 |
16.2 Evolution: Nur die Starken überleben | 408 |
16.3 Wie wir die „eXtreme Seite“ erreichten | 413 |
16.3.1 Kontinuierliche Integration | 414 |
16.3.2 Testgetriebene Entwicklung | 415 |
16.3.3 Tägliche Standup-Meetings | 415 |
16.4 Wo wir schon einmal dabei sind ... | 417 |
16.4.1 User Storys und Story Points | 417 |
16.4.2 Velocity | 418 |
16.4.3 Iterationsplanung | 419 |
16.4.4 Programmieren in Paaren | 419 |
16.4.5 Kollektives Eigentum | 420 |
16.4.6 Offenheit für Änderungen | 422 |
16.4.7 Überstunden | 423 |
16.5 Die Kunst der Evolution | 423 |
16.6 KISS und YAGNI – zwei Seiten einer Medaille | 429 |
16.7 Evolutionstheorie und Fazit | 429 |
17 Qualitätssicherung bei studiVZ | 432 |
17.1 Einführung | 432 |
17.2 Akzeptanztests | 434 |
17.3 Selenium | 435 |
17.3.1 Die Selenium-Erweiterung von PHPUnit | 437 |
17.4 Technisches Setup von studiVZ | 438 |
17.4.1 Codeumgebung | 438 |
17.4.2 Testumgebung | 439 |
17.5 Best Practices | 440 |
17.5.1 Jugendsünden | 440 |
17.5.2 Strategiewechsel | 442 |
17.6 Eine DSL muss her | 452 |
17.6.1 Interne DSL | 453 |
17.6.2 Testing_SeleniumDSL 1.0 | 454 |
17.6.3 Testing_SeleniumDSL 2.0 – ein Entwurf | 456 |
17.7 Fazit | 458 |
18 Qualitätssicherung bei Digg | 460 |
18.1 Die Ausgangssituation | 460 |
18.1.1 Unsere Probleme | 460 |
18.1.2 Code-Altlasten | 461 |
18.1.3 Wie lösen wir unsere Probleme? | 462 |
18.1.4 Ein Test-Framework wählen | 464 |
18.1.5 Mit einem Experten arbeiten | 465 |
18.2 Das Team trainieren | 465 |
18.3 Testbaren Code schreiben | 469 |
18.3.1 Statische Methoden vermeiden | 469 |
18.3.2 Dependency Injection | 472 |
18.4 Mock-Objekte | 472 |
18.4.1 Überblick | 472 |
18.4.2 Datenbank | 472 |
18.4.3 Lose gekoppelte Abhängigkeiten | 473 |
18.4.4 Beobachter für klasseninternes Verhalten | 474 |
18.4.5 Memcache | 476 |
18.4.6 Mocken einer serviceorientierten Architektur | 477 |
18.5 Der Qualitätssicherungsprozess bei Digg | 481 |
18.5.1 Testen | 481 |
18.5.2 Vorteile | 483 |
18.5.3 Herausforderungen | 484 |
18.6 Fazit | 485 |
Schlussbetrachtungen | 486 |
Literatur | 488 |
Stichwortverzeichnis | 494 |