Unit Tests sind doch weit weniger sinnvoll, als immer wieder behauptet. Es entsteht doch zusätzlicher Aufwand zur Implementierung, der Wartung von Unit Tests und die Infrastruktur muss auch erst geschaffen werden. Zudem weiß ich als guter Entwickler ohnehin, wo der Fehler auftritt. Mit guten Debugging-Skills kann ich jeden Fehler sofort finden und ausbessern. Hier eine kleine Auswertung aus der Praxis.

Rahmenbedingungen

Nachfolgend eine Architektur einer mittelgroßen Anwendung. Das User Interface wird – entsprechender einer Webanwendung – per HTML und JavaScript abgebildet. Die Daten werden über Services bezogen. Diese befragen wiederum einen Businesslogik-Layer. Die Kapselung der unterschiedlichen Datenquellen findet über Repositories statt. Die angebundenen Datenquellen sind unterschiedlicher Natur (DBMS, Sharepoint, etc.).

Architektur Web-Anwendung

Die Testing-Infrastruktur

Eine häufige Ausrede findet sich in der Erstellung einer geeigneten Testing-Infrastruktur. Oftmals wird hier jedoch der falsche Weg eingeschlagen:

  • Die Testing-Infrastruktur wird nicht zu Projektbeginn beim initialen Aufsetzen der Solution berücksichtigt. Erst zu einem späteren Zeitpunkt (vielfach nach zahlreichen Mannwochen Implementierung wird der Bedarf erkannt). Fragen bezgl. des Nachziehens von Tests, eventuell notwendigen Refactorings, um überhaupt ein Testing zu ermöglichen lassen den Aufwand nach oben treiben. Unit Tests werden nicht eingeführt, oder nur halbherzig.
  • Aufsetzen der Unit Tests als Integrationstests. Services bedürfen oft einer Konfiguration (Verbindungseinstellungen, konfigurierte Provider etc.). Vielfach wird dies über Konfigurationsdateien vorgenommen, um am Zielsystem flexibel zu sein. Durch fehlendes Know-How bezgl. Mocking oder der schlichten Nichtverwendung entstehen komplexe Gebilde, die mit Unit Tests nichts zu tun haben und die Motivation, Tests zu schreiben aufgrund des Aufwandes erheblich senken.

Die Basis für Unit Tests muss dementsprechend also bereits zu Beginn eines Projektes berücksichtigt werden. Bei einer späteren Erweiterung müssen gegebenenfalls Refactorings in Kauf genommen werden, ebenfalls gilt es zu überlegen, welche Bereiche getestet werden sollen. Eine Beschränkung darauf als auch ein striktes Einhalten können die Situation wesentlich verbessern.

Was benötige ich für meine Testing-Infrastruktur?

Unabhängig aller im Web verfügbaren Diskussion und Über-Drüber-Lösungen benötige ich als Entwickler in der Realität wenig um erfolgreich Unit Testing zu betreiben:

  • Visual Studio bietet bereits Testprojekte, welche den meisten Ansprüchen genügen werden. Natürlich gibt es hier zahlreiche Frameworks die verwendet werden können, das muss aber nicht sein. Das ergibt de facto keinen Aufwand. Der Wille als auch ein Basiswissen hinsichtlich Testing müssen vorhanden sein.
  • Services, Provider und Co. beziehen sich meist auf eingebundene Ressourcen, die ein konfiguriertes System verlangen. Unit Tests verlangen nicht das Testen dieser Ressourcen, sondern lediglich der Funktionalität, die für den Aufruf durchlaufen werden müssen (bzw. der Businesslogik). Es bietet sich daher an, Services zu mocken. Dazu stehen zahlreiche Mocking-Frameworks (moq, Rhino Mocks etc.) zur Verfügung. Die Arbeitsweise unterscheidet sich kaum. Eine erste Einarbeitungsphase kann je nach Know How zwischen 30 und 120 Minuten abgeschlossen werden.
  • Sinnvolle Basisklassen. Bestimmte Mocks bzw. Mocking-Konfigurationen werden für die meisten Tests verwendet. Gut überlegte Basisklassen erleichtern das tägliche Schreiben von Unit Tests ungemein. In der Regel entsteht hier jedoch meist laufender Aufwand durch die Erweiterung der Infrastruktur. Der initiale Aufwand beschränkt sich meist auf das Erkennen der ersten Anforderungen an die zu schreibenden Tests.

In der Regel empfiehlt es sich, mit Unit Tests zu beginnen und diese sukzessive zu erweitern. Das wirkt sich bei entsprechender Ernsthaftigkeit auf die gesamte Infrastruktur aus, womit sich diese quasi von alleine erweitert.

Die größte Hürde bei Unit Tests ist der innere Schweinehund. Ist dieser erst überwunden, schreiben sich die Unit Tests fast von alleine. Die zweite Hürde stellt sich durch die Frage nach den zu testenden Teilen der Software. Es ist wenig sinnvoll, alles testen zu wollen. Vielmehr sollte man sich auf wesentliche Bereiche der Software konzentrieren und die Stellen, welche die Basis der Anwendung ausmachen, gut zu testen.

Visual Studio bietet einem .NET Entwickler bereits viele Erleichterungen. Zusammen mit einem Mocking-Framework und einer Portion Willen können bereits gute Erfolge erzielt werden – und das ohne viel Aufwand.

Auswertung aus der Praxis

Den Aufwand für die Infrastruktur muss man als gegeben hinnehmen. Bei Bedarf kann dieser minimal gestaltet werden, was auch zielführend ist. Vielmehr zählt der laufende Aufwand zur Erstellung und Wartung von Unit Tests. Dieser ist in der Regel schwer zu beziffern. Nachfolgend jedoch eine durchgeführte Übersicht aus der Praxis, die sicherlich keine Antwort auf alle Projekte und Rahmenbedingungen darstellt, aber als guter Anhaltspunkt gesehen werden kann.

Es wurde festgestellt, dass pro QA-Durchlauf ca. 60 Bugs gefunden werden. Diese setzen sich aus UI-Bugs (unterschiedliche Browser, etc.), Fehler der Businesslogik und div. andere Fehler bei der Verarbeitung mit Fremdsystemen etc. zusammen. Je nach Bereich sind diese durch Unit Tests (Testing von JavaScript etc. wurde hier nicht berücksichtigt) feststellbar oder nicht (müssen also ohnehin manuell gefunden werden). Nachfolgend die entsprechende Liste.

Aufstellung der gefundenen Fehler

Für die betroffenen Stellen waren in diesem Fall keine Unit Tests vorhanden. So mussten die gefundenen Fehler manuell geprüft, gefunden und gefixt werden. Im Zuge dessen (der Aufwand wurde extra erfasst) wurden Unit Tests für diese Fehler implementiert (siehe feststellbar durch Unit Tests). Dies dauerte in der Regel 10 Minuten. Da die Tests sinnvoller Weise während der Implementierung geschrieben werden und hier natürlich die zu testenden Fälle erst gefunden werden müssen (bzw. Tests für den Schön- als auch den Schlechtwetterfall zu implementieren sind), wurde das 4-fache der benötigte Zeit veranschlagt. Das Ergebnis ist in der nachfolgenden Übersicht zu sehen.

Gegenüberstellung Aufwände Bugfixing und Unit Testing

Das Ergebnis zeigt einen Unterschied von 5,7 Manntagen zugunsten der Unit Tests. Alle weiteren Fälle können durch Unit Tests (einige davon schon unter Betreibung höheren Aufwandes) nicht festgestellt werden und wurden auch nicht berücksichtigt.

Die Zahlen wurden im Zuge eines Sprints von 4 Wochen und einem Team von 3 Personen ermittelt. Eine Hochrechnung auf ein Entwicklungsjahr ergibt somit eine Differenz von fast 74 Manntagen, die anderweitig eingesetzt werden können, unter der Annahme, dass sich die Zusammenstellung ähnlich verhält (was jedoch in der Praxis nicht der Fall sein wird). Nichts desto trotz ergibt sich dadurch ein klarer Vorteil für den Einsatz von Unit Tests, da hier definitiv Aufwand eingespart wird – entgegen anderweitiger Meinungen.

Fazit

Dieser Berechnung liegt nur die Erfahrung aus einem einzigen Sprint einer Entwicklungsmannschaft zu Grunde und bedarf wohl die Erfassung weiterer Sprint, um das Ergebnis zu verfeinern. Es darf jedoch davon ausgegangen werden, dass mit zunehmender Projektgröße die Aufwandsersparnis steigt. Selbst in diesem Beispiel hätten fast 6 Manntage zielbringender eingesetzt werden. Statt Fehlersuche hätten weitere Funktionalitäten entwickelt, Dokumentationen geschrieben oder Anforderungen genauer hinterfragt werden können. Der Vollständigkeit halber muss erwähnt werden, dass auch das QA-Team Zeit für das Erfassen von Fehlern, der Schritte dahin etc. benötigt. Diese Zeit wurde nicht berücksichtigt …

An dieser Stelle würden mich Eure Erfahrungen hinsichtlich Motivation, Aufwände und Umgang mit Unit Tests in der Praxis interessieren. Wie läuft das in deinem Entwicklungsteam?

Über den Autor

Norbert Eder

Ich bin ein leidenschaftlicher Softwareentwickler und Fotograf. Mein Wissen und meine Gedanken teile ich nicht nur hier im Blog, sondern auch in Fachartikeln und Büchern.

9 Kommentare

  • Pfff, bleib mir Weg mit dem neu modischen Kram! Ohne Unit-Tests sieht es in etwa so aus.

    – Kann man erst sehr spät die Funktionalität testen. Meistens erst dann, wenn man entsprechend eine GUI hat.
    – GUI anwerfen beim Debugen ist EXTREM langsam. (Immer ein guter Grund fürn neuen Kaffee)
    – GUI muss in der Regel dann konfiguriert werden (asdf, 12345 sind ausreichende Testdaten)
    – Was man einmal getestet hat wird kein zweites mal getestet (hat ja schon funktioniert von daher).
    – 10 Minuten vor einen Update wird noch einmal den Bug-Gott ein Opfer gebracht
    – 10 Minuten nach einen Update wird der erste HotFix eingespielt
    – Wenn kein Rollback gemacht werden muss, wurde das Opfer vom Bug-Gott akzeptiert
    – Ein Tag nach einen Update hat man 1-1600 Mails mit dem Betreff "Exception"
    – Anhand des Exceptionnamen weis man schon in welcher Codezeile der Fehler aufgetreten ist und wie er zu beheben ist (Übung macht den Meister)

    Woher soll ich da bitte noch die Zeit haben Unit-Tests zu schreiben!

  • In der Theorie sind Tests super, in der Praxis auch, wenn man in einem releasten Projekt einen Hotfix machen muss und viele Tests hat die einem Bestätigen, dass alles noch läuft wie gewohnt. Dazwischen ist Alltag und der ist nicht immer lustig. Ich entwickle grad eine Anwendung und ein Framework welche die Anwendung bedient. Das ganze Prozedere ist sehr agil. Fakt ist, ich komme mit dem anpassen der Tests nicht nach, so schnell veraendert sich das public API des Frameworks.
    Ergebnis: die Test-Projekte fliegen wieder mal aus der Solution und landen auf der "Gute Vorätze Liste".
    Trotzdem habe ich im Laufe der Jahre "meine" Methode gefunden um sehr/ausreichend stabile Projekte auszuliefern: die exzessive Nutzung von Assert!
    D.h. im Endergebnis: sobald mein Code die angedachten Pfade verlässt gibts eine Exception, oder anders gesehen, die meisten Tests sind im Code enthalten…..

    Leider scheint das Thema/Feature "Assert" komplett in den diversen Hypes untergegangen zu sein. Kein Autor, keine Zeitschrift, kein Seminar, keine Schule die sich um das Thema schert…
    Ich hab das Thema letztes Jahr bei Ralph W. in die Diskussion zur sinnhaftigkeit von Tests eingeworfen aber auch da ist niemand auf das Thema eingestiegen…

  • Testgetriebene Entwicklung ist nicht dafür da, um Fehler und Bugs zu finden. Es soll die Entwicklungsgeschwindigkeit erhöht werden. Wer mehr entwickelt, produzierte unter Umständen dann sogar auch mehr Bugs.

    Hier wurden vermutlich Regressiontests angefertigt, wie sie eine Q&A Abteilung anfertigen sollte.

    Aufwände von testgetriebener Entwicklung sind nur bedingt in Manntagen zu messen, da Tests mit der Implementierung verschmolzen sind. Der Wechsel von Featurecode und Testcode kann mehrfach in einer Minute geschehen.

    Macht man neben Unit-Test auch Integrationstests oder ausführbare Akzeptanztests (ATTD/BDD) ergeben sich andere Aufwände (Nachteile) und andere Nutzen (Vorteile). Es sollte aber differenziert werden.

    @Werner Mairl: Asserts im Code würde ich mal als Logging/Tracing-Technik bezeichnen. Ein Continous-Integration Server schlägt bei Fehlern vermutlich niemals darauf an. Es ist kein Sicherheitsnetz für ein Deployment. Entwicklertests sind imho komplementär zu Tracing/Logging. Das eine ersetzt das andere nicht.

  • @Rober M.
    aus meiner Erfahrung "Jein"
    ich präzisiere: Asserts sind bei mir keineTracing/Logging Technologie sondern mit Assert versuche ich jede IMPLIZITE Annahme abzusichern (explizite Annahmen feuern normale Exceptions wenn sie nicht zutreffen)
    Wenn dann die Unittests wieder einmal gelitten haben, fahre ich mit einigen Integrationstests (speziell auf der Business Logik, nicht unbedingt im UI weil schwierig) in Kombination mit den Asserts ziemlich gut – ein Erfahrungswert.
    Da wirft sich jetzt die Frage auf: in wie weit sind die Tätigkeiten "Testen" und "Implizite Annahmen sicherstellen" vergleichbar, wo liegt der Unterschied, die Differenz ?
    Darüber muss ich mal nachdenken….

  • @Robert: Die Entwicklungsgeschwindigkeit zu erhöhen ist natürlich die eine Sache. Im Gegensatz ist es aber durchaus so, dass Unit Tests die Qualität erhöhen (sollen) und somit viele Probleme bereits im Vorfeld erkannt und beseitigt werden können. Das hat natürlich auch eine Auswirkung auf Fehler, da sich deren Zahl wesentlich reduziert.

    @Markus: Kannst du deine Aussage konkretisieren? Das würde es mir ermöglichen, deine Kritik aufzunehmen und zu verwerten.

  • Lange nicht mehr so einen Haufen Mist gelesen. Der ganze Beitrag zeugt von gnadenloser Inkompetenz. Das zeigt mal wieder, dass das MVP-Programm von Microsoft absoluter Schrott ist…

  • Ich muss sagen das ich den Artikel für eher schlecht halte. Nein, ich bin nicht sonderlich erfahren mit Unit Tests, aber ich halte die Zeitangaben für komplett verkehrt – zumindest wenn man neben Projekten auch Produktentwicklung betrachtet.

    Bei einem Projekt mit begrenzter Entwicklungszeit und begrenzten Entwicklungszyklen mögen die Aussagen vielleicht stimmen (es gibt dazu aber auch gegenteilige Studien), sofern man wie ich aber in der Produktentwicklung tätig ist, sprich ein Programm über viele Jahre pflegt und weiter entwickelt, dürfte es sich wesentlich zu Gunsten der Unit Tests verschieben.

    Ich kenne es aus der derzeitigen Erfahrung ohne Unit Tests. Eine Änderung kann sich auf viele Programmstellen direkt und indirekt auswirken. Wenn man jedes mal das gesamte Programm manuell testen muss, ist dies ein enormer Aufwand. Wenn man zumindest einen Teil (sagen wir einfach 50%) der Anwendung automatisch erneut testen lassen kann, mag dies einmalig einen höheren Aufwand bedeutet, dieser reduziert sich effektiv aber mit jedem Testlauf. Und nach ein paar Durchgängen mag dieser Automatismus sogar einen Vorteil einbringen, über die Jahre hinweg mit Sicherheit.

    Zudem soll TDD ja auch die Entwickler mehr vor möglichen Fehlerquellen sensibilisieren. Sofern dies die Qualität erhöht, ist unabhängig von Unit Tests auch schon geholfen.

  • Hallo André,

    danke für deinen Kommentar.

    Die Zeitangaben stammen aus der Realität. Zugrunde liegt ein Produktentwicklungsteam. Du meinst, dass sich die Zahlen sogar weiter zu Gunsten der Unit Tests verschieben könnten. Und recht hast du. Genau das hatte ich auch versucht hervor zu heben.