Die Frage stellen sich einige Entwickler und Manager immer wieder. Nicht nur diejenigen (hoffentlich wenigen), die noch keine Unit Tests schreiben, sondern vielmehr jene, die schon seit längerem Unit Tests einsetzen. Der Hauptgrund dafür ist sicherlich die falsche Anwendung und falsche Erwartungshaltung beim einsetzen von Unit Tests.
Steve Sanderson beantwortet diese Fragen in seinem Unit Test Best & Worst Practices Artikel äußerst anschaulich. Ich möchte hier dennoch einen kurzen Abriss über die Fragestellung und seine Antworten geben.
Zunächst einmal sollte die Eingangsfrage geklärt werden: Wozu Unit Tests? Ganz einfach: Um konsequent stabile Software entwickeln zu können. Bei Unit Tests geht’s also nicht um Fehlerfindung (jeglicher Art), sondern um eine Entwicklungsmethode, von vornherein Komponenten einer Software funktional korrekt und technisch stabil zu entwickeln. Unit Tests helfen auch deutlich bei der Verwaltung und Veränderung von Komponenten (z.B. durch Extensions oder Refactoring), um Instabilitäten während Refactorings zu erkennen und zu beheben.
Gute und schlechte Unit Tests
Damit kommt man auch unweigerlich zur nächsten Problemstellung. Wie kann ich Unit Tests anwenden, dass Sie mir dabei helfen, stabile Software zu entwickeln? Einfacher grafragt Was sind “gute”, und was sind “schlechte” Unit Tests? Steve beantwortet diese Frage aus der TDD-Sicht. Für ihn sind Unit Tests besonders dann gut, wenn sie ausschliesslich eine funktionale Beschreibung der Software-Komponente abbilden. Das kann man natürlich besonders dann gut machen, wenn man nichts über die Art und Weise der Implementierung der Funktionalität kennt, also klassisches TDD Test-First betreibt.
Ein ebenso guter Test (welches man mit Unit Testing Werkzeugen abbilden kann) ist ein echter Integrations-Test. Bei dieser Art von Tests wird nicht die Software in ihren einzelnen Bestandteilen (Klassen, Komponenten) betrachtet, sondern als Ganzes aus der Sicht des Benutzers. Es werden funktionale Bereiche getestet, ohne die Implementierung der Software zu kennen. Diese Integrations-Tests sind besonders hilfreich Regressionen des Systems festzustellen.
Damit ist auch beantwortet, welche Unit Tests nun schlechte sind: alle anderen. Im Alltag spiegelt sich das durch Tests wieder, die zu sehr auf die Implementierung eingehen (z.B. ein *Returns_Not_Null Test), auf die technischen Elemente (z.B. ein *Db_User_Created Test) oder eine komische Mischung aus reinem Unit Test und Integrations-Test sind.
Unit Tests sind Katalysator für komponenten-orientierte Software
Die Beurteilung über schlechte und gute Unit Tests ist im täglichen Einsatz von Unit Tests immer wieder in den Vordergrund zu rücken, denn dadurch lässt sich auch einiges über das System-Design und die Software-Architektur sagen. Ein System, welches das schreiben und verwalten von Unit Tests leicht ermöglicht, bei dem die Unit Tests reine Funktionalitäten testen, bei dem Unit Tests nicht von anderen Tests oder sogar der Laufzeitumgebung abhängig sind ist ein deutlich auf den Grundkonzepten der Objekt- und Komponentenorientierung aufgebautes System. Die Kohäsion ist im Mikro-System sehr hoch, um Makro-System sehr niedrig. Ein Austausch einer Komponente ist problemlos, aber funktional immer wirkend.
Sind die Symptome jedoch, dass man für die Erstellung der Unit Tests schon Zielbereiche suchen muss, das Unit Tests nur rudimentäre fachliche Aussagen treffen oder das Verwalten der Test Suite mit zunehmender Test-Anzahl immer aufwendiger und schwieriger wird, dann kann man daraus ableiten, dass das System-Design der Software tendenziell nicht auf Komponentenorientierung ausgelegt ist. Überdies lässt sich weiter schlußfolgern, dass das System mit hoher Wahrscheinlichkeit die Prinzipien “Single Responsibility” und des “Divide & Conquer” missachtet.
Schmerztherapie Unit Tests
Besonders bei existierenden Systemen, bei denen die Weiterentwicklung strategisch wichtig ist und die ökonomische Effizienz gesteigert werden soll, sind echte, gute Unit Tests ein mittelfristig starkes Werkzeug, um das gesamte System effizienter und stabiler zu gestalten. Damit ist nicht nur die Funktionalität (Integrity) oder Defekt-Quote (Defect Rate) gemeint, sondern im Besonderen der Verwaltungsaufwand (Maintenance), die Weiterentwicklungskosten (Features) und die hochgelobte niedrige Reaktionslatenz zum Markt (Time To Market). Manchmal ist es schwer Unit Tests zu schreiben, weil man grundlegende Systemkomponenten anders aufbauen und strukturieren muss, um diese testbar zu machen.
Manchmal ist es schwer, Tests für Dinge zu schreiben, deren Implementierung nicht bekannt ist. Manchmal ist es schwer, die geänderte Grundfunktionalität in dutzenden Tests nochmals zu adaptieren. Doch am Ende wird man belohnt mit schnellerer Feature-Entwicklung und einer besonders vertrauenswürdigen, stabilen Software.
Unit Testing tut gut, auch wenn es weh tut.
byGMBSG: Unit Tests, TDD und Testbarkeit: Ja! at .NET Stories: Digitale ErfahrungenonNovember 3rd 2009[...] Thomas in ihren Blogs. Ich habe die Fragestellung schon vor einiger Zeit mit dem Artikel “Wozu Unit Tests?” aufgegriffen und beantwortet, möchte aus aktuellem Anlass meine bescheidene Meinung noch [...]