Softwareentwicklung: Testgetriebene Entwicklung nach der St. Pauli Schule

Die St. Pauli Schule der testgetriebenen Entwicklung ähnelt der Münchner Schule, verzichtet aber auf den komplexen Einstiegstest. Kann das gelingen?

In Pocket speichern vorlesen Druckansicht 7 Kommentare lesen

(Bild: Blackboard/Shutterstock.com)

Lesezeit: 12 Min.
Von
  • Marco Emrich
Inhaltsverzeichnis

Testgetriebene Entwicklung (Test Driven Development, TDD) ist eine beliebte und nützliche Methodik. Richtig angewendet reduziert TDD die Fehlerdichte signifikant, ohne dabei Produktivitätsverluste in Kauf nehmen zu müssen. Vor allem hilft TDD, Softwaredesign zu verbessern.

Der Artikel über die Münchner Schule auf heise Developer hat die unterschiedlichen Stile beziehungsweise Schulen für testgetriebene Entwicklung aufgezeigt. Sechs Vertreter stechen besonders heraus, da sie bekannt und ihre Konzepte gut dokumentiert sind:

Die St. Pauli Schule ist mit der Münchner Schule verwandt. Anfangs war sie nur als scherzhafte Antwort darauf gedacht, hat sich aber mittlerweile zu einem eigenen nützlichen TDD-Stil entwickelt. Die St. Pauli Schule zeichnet sich durch sechs Regeln aus:

  • Starte auf dem API-Level,
  • wachse langsam und stetig,
  • delegiere Unterprobleme an Stubs,
  • ersetze Stubs rekursiv,
  • überprüfe die Allgemeinheit der Lösung durch einen Validierungstest und
  • behandle die Test-Suite als "append-only" (nachträgliche Veränderungen sind nicht zulässig).

Einer der wesentlichen Unterschiede zur Münchner Schule besteht im ersten Testfall. Die Münchner Schule verwendet einen komplexen Anfangstest, der den größten Teil der Anforderungen aus der Problemstellung abdeckt. St. Pauli möchte dagegen wie London oder Chicago mit einem möglichst einfachen Testfall beginnen.

Um die Vorgehensweise der St. Pauli Schule zu verdeutlichen, bietet sich wie im Artikel zur Münchner Schule die bekannte Programmierübung Diamond-Kata an, die auch der Gründer der St. Pauli Schule Steven Collins als Beispiel verwendet. Die identische Problemstellung hilft dabei, die Unterschiede zur Münchner Schule besser zu erkennen. Als Programmiersprache kommt JavaScript zum Einsatz und als Testframework Jest. Die Übung hat folgende Aufgabenstellung:

Ziel: Zu einem gegebenen Buchstaben wird eine Raute ausgegeben, die mit "A" beginnt und den angegebenen Buchstaben an der breitesten Stelle hat.

diamond("B") erzeugt

.A.
B.B
.A.

diamond("C") gibt Folgendes aus:

..A..
.B.B.
C...C
.B.B.
..A..

Hinweis: Zur besseren Lesbarkeit verwendet der Beispielcode Punkte (.) statt Leerzeichen.

Die erste Regel besagt: "Starte auf dem API-Level". Dabei ist nicht etwa eine API im Sinne von REST oder einer speziellen Schnittstelle gemeint. Stattdessen ist die API abhängig vom jeweiligen SUT (Subject under Test). Es kann sich unter anderem um einen Service, eine Komponente oder eine Reihe von Funktionen handeln. Die Abstraktionshöhe ist von Fall zu Fall unterschiedlich.

Während die Münchner Schule mit einem komplexeren Fall wie dem Diamanten zu "C" anfängt, beginnt St. Pauli mit dem einfachen "A"-Diamanten:

describe("diamond", () => {
  it("should return the A-Diamond ", () => {
    expect(diamond("A")).toEqual("A");
  });
});

Der nächste Schritt ist ähnlich zu München: Statt einer echten Implementierung sorgt ein Fake beziehungsweise Stub dafür, dass der Code den Test besteht. Der Begriff Stub ist etwas anderes als der Mock-ähnliche Test-Stub aus der Kategorie Test-Double. Ein Fake oder Stub ist ein Stück Produktionscode, der für den speziellen Testfall die richtige Lösung vortäuscht:

const diamond = (letter) => "A";

Es folgt der nächste Testfall auf API-Level:

it("should return the B-Diamond ", () => {
  expect(diamond("B")).toEqual(".A.\nB.B\n.A.");
});

Für diesen Fall ist es erforderlich, den Code zu erweitern:

const diamond = (letter) => {
  switch (letter) {
    case "A":
      return "A";
    case "B":
      return ".A.\nB.B\n.A.";
  }
};

Sobald zwischen den Fällen ein Muster erkennbar ist, lässt es sich durch Refactoring extrahieren. Da das noch nicht der Fall ist, folgt ein dritter Test mit dem dazugehörigen Fake für den "C"-Fall.

it("should return the C-Diamond ", () => {
  expect(diamond("C")).
    toEqual("..A..\n.B.B.\nC...C\n.B.B.\n..A..");
});

const diamond = (letter) => {
  switch (letter) {
    case "A":
      return "A";
    case "B":
      return ".A.\nB.B\n.A.";
    case "C":
      return 
        "..A..\n.B.B.\nC...C\n.B.B.\n..A..";
  }
};

In dem Code sind unterschiedliche Muster erkennbar, die ein Refactoring extrahieren kann. Kent Beck nennt das Prinzip, bei dem man erst mehrere Fälle über Verzweigung "faked" und anschließend Gemeinsamkeiten herauszieht, Triangulation [1]. Es ist der treibende Motor der St. Pauli Schule.