Foundations for Professionals .NET Professionals im Profil guide to C# guidgen.de

Blog

Nutze den Augenblick
und teile der Welt mit, was Du zu sagen hast.

Vom Saulus zum Paulus

Mittwoch, 31. März 2010, 19:39 Uhr
Permalink | Kommentare (2) | Kommentare als RSSRSS

Am 1. November 2009 habe ich den Blogeintrag Wie viel Sinn machen Unittests? verfasst, in dem ich mich skeptisch gegenüber dem Einsatz von Unittests geäußert habe – und empörte Reaktionen geerntet habe.

Meine Skepsis gründete sich im Wesentlichen auf drei Kritikpunkte, die im folgenden noch einmal aufgelistet werden:

  • Der bestehende objektorientierte Aufbau ist unter OOP- und stilistischen Aspekten sauber umgesetzt, läuft einer guten Testbarkeit allerdings zuwider.
  • Es bestehen zahlreiche Abhängigkeiten zu externen Komponenten, die sich nicht oder nur mit sehr viel Aufwand simulieren lassen.
  • Es bestehen Abhängigkeiten zur konkreten Laufzeitumgebung, die sich nicht oder nur mit sehr viel Aufwand nachbilden lassen.

Wie sich inzwischen herausgestellt hat, können alle Aspekte zufriedenstellend gelöst werden. Letztlich läuft es im Zweifelsfall in der Regel darauf hinaus, eine zusätzliche Abstraktion einzuführen, um besser entkoppeln zu können – schließlich ermöglicht erst ein gewisses Abstraktionsniveau den sinnvollen Einsatz von Stubs und Mocks.

Doch auch wer – wie ich – helfende Hände gereicht bekommt und es daraufhin geschafft hat, alle technischen Hindernisse aus dem Weg zu räumen, mag noch über den Sinn von Unittests grübeln: Die Frage lautet also nicht, wie viel, sondern warum der Einsatz von Unittests überhaupt Sinn ergibt?

Auf diese Frage möchte ich heute eingehen. Da die Entwicklung einer Anwendung irgendwann bei Null auf der grünen Wiese beginnt, liegt es nahe, sich zunächst über den Einsatz von Unittests bei neu zu schreibendem Code Gedanken zu machen: Hier wirken Unittests häufig als Bremse – schließlich müssen neben dem eigentlichen produktiven Code noch zahlreiche Unittests geschrieben werden.

Wer dieser Aussage in dieser Form ohne Widerspruch zustimmt, tappt in die gleiche Falle wie ich vor einigen Monaten: Er vergisst, dass dieser neu geschriebene produktive Code gründlich getestet werden muss – wenn nicht vom Entwickler, dann hoffentlich von irgendjemandem.

Hier liegt der Hund bereits begraben: Wenn nicht der Entwickler selbst sich um das Testen kümmert, muss es jemand anderes machen – der nicht weiß, was sich der Entwickler im Detail gedacht hat, warum manche Entscheidungen so und nicht anders getroffen wurden, der auch nicht weiß, welche potenziellen Sonderfälle berücksichtigt werden müssen.

Zudem weiß dieser jemand auch nicht, ob er alle potenziellen Codepfade getestet hat – er kennt schließlich die Interna nicht. Er kann nur hoffen, alles abgedeckt zu haben, weiß es aber nie.

Schließlich werden Tests, die erst zu einem relativ späten Zeitpunkt durchgeführt werden, in der Regel nicht automatisiert durchgeführt: Die Durchführung hängt also von der Tagesform des Testers ab und kann – unbeabsichtigt – schwanken und eventuell bei zwei Testläufen in verschiedenen Ergebnisse resultieren.

Kurzum: Die Qualität der Tests ist nicht so hoch, wie sie sein könnte. Die Quantität stimmt gegebenenfalls nicht. Und die Ergebnisse sind weder reproduzierbar noch verlässlich.

Wird hingegen von Anfang an mit Unittests entwickelt, ergeben sich all diese Probleme erst gar nicht: Der Entwickler weiß, was zu testen ist, er kennt die Spezialfälle, er weiß oder kann zumindest herausfinden, wie hoch die Testabdeckung ist, und die Testergebnisse sind – da die Unittests automatisiert ausgeführt werden – jederzeit reproduzierbar.

Außerdem entsteht zwar zusätzlicher Aufwand für das Schreiben der Unittests, aber die Ausführung erfordert kaum noch Aufwand. Da Unittests nur einmal geschrieben, aber immer und immer wieder ausgeführt werden, ergibt sich mittelfristig eine deutliche Zeitersparnis – bei qualitativ besseren Ergebnissen.

Aber es kommt noch besser: Da zumindest bei der testgetriebenen Entwicklung gilt, dass immer nur gerade so viel Code geschrieben werden sollte, wie notwendig ist, um einen Unittest zu erfüllen, wird zum einen nur Code geschrieben, der auch getestet ist, zum anderen wird aber auch nur jener Code geschrieben, der tatsächlich erforderlich ist. Das heißt, der Einsatz von testgetriebener Entwicklung hilft, das YAGNI-Prinzip umzusetzen.

Nun kommt aber in jedem Projekt irgendwann der Zeitpunkt, an dem nicht mehr nur neuer Code hinzugefügt, sondern ab dem auch bestehender Code geändert werden muss. Sei es, weil sich Anforderungen geändert haben oder weil Fehler behoben werden müssen.

In jedem Fall muss bestehender Code geändert werden. Typisch für gewachsene Projekte ist, dass die ursprünglichen Entwickler gar nicht mehr verfügbar sind, oder sich nicht mehr genau an spezielle Implementierungsdetails erinnern können. Da zudem in den meisten Softwareprodukten innere Abhängigkeiten bestehen, findet eine Änderung von Code selten isoliert statt, sondern beeinflusst anderen Code ebenfalls – mal mehr, mal weniger.

Das Problem liegt dann meistens darin, dass Änderungen, die im guten Glauben auf Korrektheit durchgeführt werden, Seiteneffekte haben, die ihrerseits neue Fehler produzieren. Irgendwann werden aus diesem Grund keine Änderungen mehr durchgeführt, sondern vermehrt Workarounds geschrieben – und irgendwann dann Workarounds um die Workarounds, und wieder später Workarounds um die Workarounds um die Workarounds, und so weiter …

Was also fehlt, ist ein Sicherheitsnetz, das den Entwickler beim Auftreten von unerwünschten Seiteneffekten auffängt und ihn darauf hinweist, dass seine Änderung an anderer Stelle ungewollte Auswirkungen hat. Genau dies erfüllen Unittests, wenn sie gepflegt werden und stets sämtlichen Code abdecken.

Das ist meines Erachtens der größte Vorteil von Unittests: Sie erlauben es Entwicklern, Code unbefangen ändern zu können, ohne Befürchtungen wegen Nebenwirkungen haben zu müssen.

Fasst man all diese Vorteile zusammen, so überwiegen die Vorteile ganz deutlich den initialen Aufwand, sich in Unittests einzuarbeiten und die eigene Arbeitsweise auf testgetriebene Entwicklung umzustellen: Unittests vereinfachen die Entwicklung und helfen, sie zielgerichteter und sicherer zu gestalten.

Auch wenn der Anfang schwer ist und eine saubere testgetriebene Entwicklung viel Reflektion und Disziplin erfordert – es lohnt sich.

Kommentare

Kommentar schreiben


(Zeigt dein Gravatar icon)  

  Country flag

biuquote
  • Kommentar
  • Live Vorschau
Loading