Dogmatisches TDD

Standard

Immer wieder wird den “Verfechtern” des Test-Driven Developments (TDD) vorgeworfen, dogmatisch zu sein. Vor ein paar Wochen gab es eine Diskussion unter Größen der Software-Entwicklung darüber, wann TDD und Unit-Tests verwendet werden sollen und wann nicht. Hier eine kurze Zusammenfassung.

Die Start-Up-Falle

Alles begann mit einem Blogpost von Robert C. Martin, auch bekannt als Uncle Bob. In diesem Blogpost beschrieb er die Start-Up-Falle. Das ist die Idee, dass die Situtation in einem Start-Up anders wäre. Dass alles, was man über professionelle Software-Entwicklung gelernt hat, in diesem besonderen Job nicht anwendbar ist und man daher die Regeln brechen muss.

Uncle Bob widerspricht dem vehement. Die Start-Up-Phase ist nicht anders. Sie gibt vielmehr die Richtung für die nächsten Phasen vor. Die Praktiken, die zu erfolgreicher Software führen, sind immer gültig.

Natürlich meint er mit einer dieser Praktiken TDD (Test Driven Development). Manche glauben, dass man schneller ist, wenn man keine Tests schreibt.

Uncle Bob setzt dem entgegen, dass der Buchhalter eines Start-Ups unter noch größerem Druck steht als jeder Software-Entwickler. Immerhin verwaltet er oder sie das Geld der Investoren. Dennoch folgt der Buchhalter den Regeln und der Methoden der Buchhaltung. Sind Fehler im Code tolerierbarer als die Berechnungen des Buchhalters? Natürlich nicht. Kann ein Fehler im Code das Unternehmen nicht ruinieren? Natürlich kann er das. Wieso sollte man dann auf TDD verzichten, wenn der Buchhalter auch nicht auf die doppelte Buchhaltung verzichtet?

TDD nicht anzuwenden, macht einen nicht schneller, sondern vielmehr langsamer. Die Gründe dafür sind einfach:

  • Man kann den Code nicht mehr refaktorisieren.
  • Daher wird der Code verrotten.
  • Er wird daher immer schwerer zu handhaben.
  • Man wird langsamer.

Das Gemeine daran, TDD nicht anzuwenden, ist, dass es einem das Gefühl gibt, schneller zu sein. Man arbeitet hart, ist 60, 70 oder 80 Stunden pro Woche in der Arbeit.

Aber Aufwand ist nicht gleich Geschwindigkeit. Mit der Zeit steigen die Schätzungen. Es wird immer schwerer, neue Features einzubauen. Es sammeln sich immer mehr Bugs an. Der Entwicklungsfortschritt wird immer langsamer, die Anzahl der Fehler steigt. Die Katastrophen werden immer mehr.

Daher empfiehlt Uncle Bob: Wenn man schneller vorankommen will,

  • folge den bewährten Praktiken!
  • schreibe deine Tests!
  • refaktorisiere deinen Code!
  • halte den Code einfach und sauber!
  • hetze nicht!

Die Pragmatik des TDD

Der Blogpost über die Start-Up-Falle dürfte einigen Aufruf verursacht haben. Deshalb hat Uncle Bob einen Tag später den nächsten Blogpost verfasst. Darin geht er auf den Vorwurf des Dogmatismus ein.

Im Prinzip wurde Uncle Bob vorgeworfen, dass er zu dogmatisch sei. TDD mag in manchen Fällen großartig sein, aber in anderen Fällen hätte es zu hohe Kosten. Deshalb muss man pragmatisch sein und je nach Situation entscheiden.

Für Uncle Bob ist TDD eine Disziplin für Programmierer wie die doppelte Buchhaltung für Buchhalter oder die sterile Vorgehensweise für Chirurgen. Selbst diese gehen in bestimmten Situationen von diesen Praktiken ab.

Und genauso ist es mit TDD für Programmierer. Uncle Bob listet Fälle auf, in denen nicht einmal er TDD anwendet:

  • Er schreibt keine Tests für Getter und Setter.
  • Er schreibt keine Tests für Member-Variablen.
  • Er schreibt keine Tests für triviale Funktionen oder Einzeiler.
  • Er schreibt keine Tests für GUIs. Er bemüht sich aber, jede signifikante Verarbeitung im GUI-Code in testbare Module auszulagern (MVVM, MVC, MVP, …)
  • Er wendet TDD nicht für Code an, den er noch mit der Methode “Versuch und Irrtum” entwickeln muss. Er schreibt entweder die Tests danach oder löscht den so entstandenen Code und entwickelt ihn mit TDD neu.
  • Er schreibt in manchen Situationen keine Tests für Programme, die klein sind und wirklich nur einmal verwendet werden.
  • Er mockt Software von anderen Herstellern und testet seinen Code. Nur wenn er glaubt, dass die Fremdsoftware nicht funktioniert, oder Mocken zu viel des Guten wäre, testet er diese auch.

Tests für trivialen Code?

Dieser Blogpost wiederum führte zu weiteren Diskussionen über die Frage, ob trivialer Code getestet werden soll. Immerhin hat Uncle Bob geschrieben, dass er Getter, Setter, triviale Funktionen und Einzeiler nicht testet. Mark Seemann vertritt die Position, dass auch trivialer Code getestet werden muss. Seine Argumente:

  • Bei TDD lenken die Tests das Design. Wenn man den Test schreibt, kann man noch gar nicht wissen, ob der Code trivial wird. Er untermauert dieses Argument mit Uncle Bobs Idee der Transformation Priority Premise.
  • Trivialer Code muss nicht trivial bleiben. Wenn ein Getter oder Setter bzw. eine .NET-Property trivial ist, wofür hat man sie dann überhaupt? Getter und Setter ermöglichen es, in Zukunft die Implementierung zu ändern. Um diese Implementierung refaktorisieren zu können, braucht man Unit-Tests.
  • Nicht-trivialen Code nicht zu testen, ist ein schrecklicher Rat für TDD-Anfänger. TDD-Anfänger werden diese Hintertüre jedesmal verwenden, wenn der Code einmal schwieriger zu testen ist. Die Bedingung ist ja auch nicht besonders genau definiert. Wann kann man schon vorhersehen, dass der Code trivial sein wird?

Weiters demonstriert Mark Seemann an einem Beispiel, dass man den Aufwand für das Testen von Properties reduzieren kann. Seine These ist, dass Uncle Bob die Kosten für das Testen von trivialen Code als fix annimmt und den Nutzen dafür als gering. Wenn man aber die Kosten senkt, ergibt sich ein besseres Kosten-/Nutzenverhältnis.

Dem widersprach Mark Rendle. Seine Argumente:

  • Tests für .NET-Properties testen den Compiler und nicht den Code.
  • Sie testen Implementierungsdetails und nicht die Anwendungslogik. Es gibt andere Tests, die eine Anwendungsfunktionalität testen, die die .NET-Properties, Getter und Setter brauchen, sonst müssten sie als toter Code entfernt werden.
  • Sinnlose Tests sind schlimmer als überhaupt keine Tests.
  • Tests für Getter, Setter und anderen trivialen Code verbergen überflüssigen Code.
  • Sie präsentieren TDD in schlechtem Licht, weil sie TDD als unnötig zeitfressend, kleinlich und zwecklos erscheinen lassen.

Daraufhin hat Derick Bailey beiden widersprochen und rechtgegeben. Für ihn ist die Frage, ob eine .NET-Autoproperty getestet werden soll, mit der eigentlichen Funktion des Codes verbunden. Wenn die Property einen Mehrwert liefert, was die Funktionalität betrifft, sollte es auch einen Unit-Test dafür geben.

Jimmy Bogard hat wahrscheinlich unabhängig von der ganzen Diskussion ungefähr zur gleichen Zeit einen Blogpost geschrieben. Kurz gesagt, soll man nur dann Tests schreiben, wenn sie einen Wert bieten. Und wann das ist, beantwortet er mit der berühmten Beraterantwort: “It depends.”

In einem weiteren Blogpost präzisierte Mark Seemann seine Position. In Übereinstimmung mit dem Dreyfus-Modell bevorzugt er fixe Regeln für TDD-Anfänger. Weiters wirft er die Frage auf, ob in seinem Beispiel überhaupt eine .NET-Property notwendig ist. Eigentlich würde auch ein normales Feld reichen.

Start-Ups und TDD

In der Zwischenzeit hat Greg Young auf einen anderen Aspekt des ursprünglichen Blogposts von Uncle Bob geantwortet. Anhand eines eigenen persönlichen Beispiels versucht er dabei zu argumentieren, dass es für Start-Ups gefährlich sei, das Produkt nach den üblichen Entwicklungsmethoden zu entwickeln. Ein Start-Up sollte sich vielmehr darauf konzentrieren, “Production Prototypes” zu entwickeln.

Chris McKenzie hat ihm widersprochen und die Frage aufgeworfen, ob es gerechtfertigt ist, mangelhafte Software am laufenden Band zu produzieren, nur weil man für ein Start-Up arbeitet.

Uncle Bob antwortete in einem eigenen Blogpost und wehrt sich vor allem gegen die Unterstellung, dass TDD jemanden langsamer entwickeln lässt.

Meine Sichtweise

Was halte ich eigentlich von dem allen?

Ich stimme der Position von Uncle Bob zu. TDD ist eine bewährte Methode, doch gibt es Situationen, wo sie nicht praktikabel ist. Allerdings handelt es sich dabei um Ausnahmen, die es so lange wie möglich zu vermeiden gilt.

Ich stimme auch Mark Seemann zu, dass es für TDD-Anfänger fixe Regeln braucht. Nur mit viel Erfahrung kann man Regeln brechen. Deshalb empfehle ich jedem TDD-Anfänger, den Regeln zu folgen. Auch dann, wenn jemand mit TDD-Erfahrung diese Praktik nicht anwenden würde.

Ich habe noch nie in einem Start-Up gearbeitet, aber ich verwende seit ca. 10 Jahren TDD. Und ich entwickle deshalb nicht langsamer. Greg Young beschreibt in seinem Blogartikel einen Fall von technischen Schulden. Um kurzfristige Ziele zu erreichen, kann man auf gutes Design und Unit-Tests verzichten. Allerdings darf man nicht vergessen, rechtzeitig den Code auf eine tragfähige Basis mit Tests zu stellen, sonst verrottet der Programmcode, neue Features können nur mehr mit großem Aufwand eingearbeitet werden und die Fehlerquote steigt. Genauso wie es Uncle Bob in seinem ursprünglichen Blogpost beschrieben hat. Kann es sich ein junges Unternehmen, dass sich noch einen Namen erarbeiten muss, überhaupt leisten, laufend mangelhafte Software auszurollen?

Leave a Reply

Your email address will not be published. Required fields are marked *