Von Rot zu Grün – Green Bar Patterns

Standard

Ich wende testgetriebene Entwicklung bereits seit über 10 Jahren an. Kurz zur Erinnerung: Bei TDD (Test-Driven Development) erfolgt die Implementierung in vielen kleinen Iterationen, die aus drei Teilen bestehen:

  • Schreibe einen Test für das erwünschte Verhalten. Der Programmcode erfüllt diesen Test noch nicht oder es gibt ihn noch gar nicht. Manche Testrunner stellen diesen Zustand durch einen roten Balken dar. Daher wird diese Phase oft mit Red bezeichnet.
  • Ändere oder schreibe den Programmcode mit dem kleinstmöglichen Aufwand, damit er den Test erfüllt. Manche Testrunner stellen diesen Zustand durch einen grünen Balken dar. Daher wird diese Phase oft mit Green bezeichnet.
  • Refaktorisiere den Code. Entferne Code-Duplizierung, führe Abstraktionen ein, wenn nötig, usw. Danach führe die Tests nochmal aus. Der Code muss die Tests noch immer erfüllen. Diese Phase wird oft mit Refactor bezeichnet.

Ich habe am Beginn viel über TDD gelesen. Meistens waren es Artikel im Internet. Es gab noch nicht viele Bücher über dieses Thema. Eine Ausnahme war Kent Becks Test-Driven Development. Es ist ein vergleichsweise dünnes Buch, das ich sehr schnell gelesen hatte.

Vor kurzem habe ich das Buch wieder gelesen und dabei wurden mir einige Details wieder bewusst. Dazu gehören die sogenannten “Green Bar Patterns”. Diese Patterns beschreiben, wie wir den Programmcode ändern können, damit er die Tests erfüllt:

  • Triangulation
  • Fake It (‘Til You Make It)
  • Offensichtliche Implementierung

Triangulation

Bei der Triangulation verallgemeinern wir den Code erst, wenn wir zwei oder mehr Tests geschrieben haben. Daraus ergibt sich folgender Ablauf:

  • Zuerst schreiben wir einen Test.
  • Dann machen wir die kleinstmögliche Änderung am Programmcode, damit er diesen Test erfüllt, z.B. geben wir nur den erwarteten Wert zurück.
  • Dann schreiben wir noch einen Test.
  • Erst jetzt abstrahieren wir die Implementierung.

Nehmen wir an, dass wir eine Funktion schreiben wollen, die zwei Zahlen addiert. Der erste Test könnte so aussehen:

    [Test]
    public void Sum_3plus4_Returns7()
    {
      Assert.That(Plus(3, 4), Is.EqualTo(7));
    }

Dann würde diese Implementierung ausreichen, um den Test zu erfüllen:

    public int Plus(int a, int b)
    {
      return 7;
    }

Um den Code verallgemeinern zu können, benötigen wir einen weiteren Test:

    [Test]
    public void Sum_9plus15_Returns24()
    {
      Assert.That(Plus(9, 15), Is.EqualTo(24));
    }

Jetzt können wir die allgemeine Implementierung schreiben:

    public int Plus(int a, int b)
    {
      return a + b;
    }

Beide Tests müssen zu diesem Zeitpunkt funktionieren.

Triangulation wird gerne verwendet, weil die Regeln sehr klar sind. Kent Beck empfiehlt diese Methode nur dann, wenn man sich über die korrekte Implementierung völlig unsicher ist.

Fake It (‘Til You Make It)

Auch bei “Fake It” machen wir nach dem ersten Test nur die kleinstmögliche Änderung am Programmcode, um den einen Test zu erfüllen. Im Unterschied zur Triangulation schreiben wir kein zweites Testbeispiel, sondern verallgemeinern den Code im Refaktorisierungsschritt.

  • Zuerst schreiben wir einen Test.
  • Dann machen wir die kleinste Änderung am Programmcode, damit dieser den Test erfüllt, z.B. durch Rückgabe des erwarteten Werts.
  • Jetzt abstrahieren wir die Implementierung durch Refaktorisierung.

Der erste Test und die zugehörige Implementierung für das obige Beispiel sieht wieder so aus:

    [Test]
    public void Sum_3plus4_Returns7()
    {
      Assert.That(Plus(3, 4), Is.EqualTo(7));
    }

    public int Plus(int a, int b)
    {
      return 7;
    }

Im Refaktorisierungsschritt eliminieren wir die Code-Duplizierung zwischen Test und Implementierung und ersetzen sie durch den richtigen Code:

    public int Plus(int a, int b)
    {
      return a + b;
    }

Wichtig bei “Fake It” ist, den Refaktorisierungsschritt ernst zu nehmen. Nach meiner Beobachtung wird gerne aufs Refaktorisieren vergessen, obwohl es einen so wichtigen Beitrag zum Software-Design darstellt.

Offensichtliche Implementierung

Bisher bestand die Implementierung nach dem ersten Test daraus, dass wir den erwarteten Rückgabewert als Konstante zurückgeben. Aber müssen wir das tun, wenn doch ganz klar ist, wie die Implementierung aussieht? Vor allem in unserem Beispiel mit der Addition zweier Parameter? Natürlich nicht. Wenn vollkommen klar ist, wie die Implementierung aussehen soll, schreiben wir sie einfach.

In unserem Beispiel sieht das so aus:

    [Test]
    public void Sum_3plus4_Returns7()
    {
      Assert.That(Plus(3, 4), Is.EqualTo(7));
    }

Und dann schreiben wir gleich:

    public int Plus(int a, int b)
    {
      return a + b;
    }

Wann verwendet man welche Methode?

Viele TDD-Neulinge fangen mit der offensichtlichen Implementierung an und verzetteln sich dann, weil sie zu große Schritte machen. Andere tendieren zur Triangulation und wundern sich, warum TDD so mühsam ist.

Tatsächlich haben alle drei Methoden ihre Berechtigung. Wir können mit “Fake It” anfangen und zur offensichtlichen Implementierung wechseln, wenn wir merken, dass der Code sehr einfach ist. Wenn die Tests öfter unbeabsichtigt rot bleiben, dann sind wir zu schnell unterwegs und sollten zu “Fake It” oder Triangulation wechseln.

Oft wissen wir nach längerem Nachdenken noch nicht, wie der Code aussehen soll. Dann werden wir mit der Triangulation beginnen und wenn wir sicherer geworden sind, zu einer schnelleren Methode wechseln.

Wenn der Code Schleifen oder Verzweigungen enthalten soll, dann ist er sehr wahrscheinlich nicht mehr einfach. Offensichtliche Implementierung ist dann offensichtlich die falsche Methode.

Leave a Reply

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