Kann die TDD-Methode von oben nach unten angewendet werden?

13

Ich bin nicht sicher, wie TDD, die Methode, den folgenden Fall behandelt. Angenommen, ich möchte den Mergesort-Algorithmus in Python implementieren. Ich beginne mit dem Schreiben

assert mergesort([]) === []

und der Test schlägt fehl mit

NameError: Name 'mergesort' ist nicht definiert

Ich füge dann hinzu

def mergesort(a):
    return []

und mein Test besteht. Als nächstes füge ich hinzu

assert mergesort[5] == 5

und mein Test schlägt fehl mit

AssertionError

womit ich zurechtkomme

def mergesort(a):
    if not a:
        return []
    else:
        return a

Als nächstes füge ich hinzu

assert mergesort([10, 30, 20]) == [10, 20, 30]

und ich muss jetzt versuchen, diesen Pass zu machen. Ich "kenne" den Mergesort-Algorithmus und schreibe:

def mergesort(a):
    if not a:
        return []
    else:
        left, right = a[:len(a)//2], a[len(a)//2:]
        return merge(mergesort(left)), mergesort(right))

Und das scheitert mit

NameError: name 'merge' ist nicht definiert

Hier ist die Frage. Wie kann ich mergemit TDD loslegen und mit der Implementierung beginnen ? Es scheint, als könnte ich nicht, weil ich diesen "hängenden" nicht erfüllten, fehlgeschlagenen Test für habe mergesort, der nicht bestanden wird, bis er mergebeendet ist! Wenn dieser Test herumhängt, kann ich TDD nie wirklich durchführen, da ich während der Erstellung meiner TDD-Iterationen nicht "grün" bin merge.

Es scheint, als ob ich bei den folgenden drei hässlichen Szenarien festgefahren bin und möchte wissen, (1) welches davon die TDD-Community bevorzugt oder (2) gibt es einen anderen Ansatz, den ich vermisse? Ich habe mir mehrere Walkthroughs zu Onkel Bob TDD angesehen und kann mich nicht erinnern, zuvor einen solchen Fall gesehen zu haben!

Hier sind die 3 Fälle:

  1. Implementieren Sie die Zusammenführung in einem anderen Verzeichnis mit einer anderen Testsuite.
  2. Machen Sie sich keine Sorgen, dass Sie beim Entwickeln der Hilfefunktion grün sind. Verfolgen Sie einfach manuell, welche Tests Sie wirklich bestehen möchten.
  3. Kommentieren Sie aus (GASP!) Oder löschen Sie die Zeilen in mergesortdiesem Aufruf merge. Setzen Sie mergesie dann wieder ein, nachdem Sie zur Arbeit gekommen sind.

Diese sehen alle albern aus (oder sehe ich das falsch?). Kennt jemand den bevorzugten Ansatz?

Ray Toal
quelle
2
Ein Teil des Ziels von TDD ist es, Ihnen bei der Erstellung eines Software-Designs zu helfen. Ein Teil dieses Designprozesses besteht darin, herauszufinden, was erforderlich ist, um das gewünschte Ergebnis zu erzielen. Im Fall von mergesort, da es bereits ein sehr gut definierten Algorithmus wird dieser Erkennungsprozess nicht erforderlich ist , und es wird dann eine Frage der Zuordnung , was Sie bereits wissen , dass das Design auf eine Reihe von Unit - Tests zu sein. Vermutlich geht Ihr Top-Level-Test davon aus, dass Ihre getestete Methode eine unsortierte Sammlung akzeptiert und eine sortierte zurückgibt ...
Robert Harvey,
1
... Nachfolgende Komponententests würden sich schrittweise mit der tatsächlichen Mechanik von a befassen mergesort. Wenn Sie nach dem "richtigen" Weg suchen, gibt es keinen anderen, als die Zuordnung des mergesortAlgorithmus zu einer Reihe von Komponententests zu präzisieren. dh sie sollten reflektieren, was ein mergesorteigentlich tut.
Robert Harvey
4
Design wächst nicht allein durch Unit-Tests. Wenn Sie erwarten, dass ein mergesortDesign auf natürliche Weise aus dem Rot-Grün-Refaktor hervorgeht, geschieht dies nur, wenn Sie den Prozess auf der Grundlage Ihres vorhandenen Wissens über steuern mergesort.
Robert Harvey
1
In TDD mergemuss nur auf der "Refactoring" -Stufe erfunden werden. Wenn Sie sehen, dass diese mergeMethode zum Bestehen eines mergesortTests eingeführt werden kann, lassen Sie Ihre Tests zunächst ohne mergeMethode bestehen. Dann überarbeiten Sie Ihre Implementierung, indem Sie eine mergeMethode einführen .
Fabio

Antworten:

13

Hier sind einige alternative Möglichkeiten, um Ihre Optionen anzuzeigen. Aber zuerst die Regeln von TDD, von Onkel Bob mit Nachdruck von mir:

  1. Sie dürfen keinen Produktionscode schreiben, es sei denn, es wird ein fehlerhafter Einheitentest bestanden.
  2. Sie dürfen nicht mehr von einem Komponententest schreiben, als zum Scheitern ausreicht. und Kompilierungsfehler sind Fehler.
  3. Sie dürfen nicht mehr Seriencode schreiben, als ausreicht , um den einen fehlgeschlagenen Komponententest zu bestehen.

Eine Möglichkeit, Regel 3 zu lesen, besteht darin, dass Sie die mergeFunktion zum Bestehen des Tests benötigen , damit Sie ihn implementieren können - jedoch nur in seiner grundlegendsten Form.

Alternativ können Sie den Zusammenführungsvorgang zunächst in eine Zeile schreiben und ihn dann in eine Funktion umgestalten, nachdem der Test ausgeführt wurde.

Eine andere Interpretation ist, dass Sie Mergesort schreiben und wissen, dass Sie eine mergeOperation benötigen (dh, es ist nicht YAGNI, was die "ausreichende" Regel zu verkürzen versucht). Daher sollten Sie mit Tests für die Zusammenführung begonnen haben und erst dann mit Tests für die Gesamtsorte fortfahren.

kdgregory
quelle
Das sind wirklich gute Beobachtungen. Ich hatte früher über das Inline-and-Factoring nachgedacht, aber wie mergeüberraschend unübersichtlich ist, machte die Idee, es als separate Funktion auszuführen, mehr Sinn. Der Stil, es in seiner Grundform inline zu machen und es dann auf der Blue-Hat-Bühne auszublenden, scheint jedoch wirklich richtig und genau das zu sein, wonach ich gesucht habe.
Ray Toal
@RayToal - Ich neige tatsächlich dazu, die mergeOperation vor dem Sortieren vollständig zu testen (und die Operation separat zu testen partition). Ich denke, dass die behaupteten Vorteile des aufstrebenden Designs darin liegen, langsam auf ein bekanntes Ziel hinzuarbeiten. Im Falle von Mergesort denke ich nicht, dass das Ziel im Allgemeinen die Sortierung ist (weil dann die Blasensortierung enden wird). Sie kennen die grundlegenden Operationen und arbeiten auf diese Operationen hin. Die Sorte ist meist ein nachträglicher Einfall.
kdgregory
1
Es gibt eine vierte Option. Übergeben Sie die mergeFunktion mergesortund verspotten Sie ihr Verhalten. Dann gehen Sie zurück und implementieren mergeTest zuerst. Die Delegierten sind fantastisch ™.
RubberDuck
@RubberDuck Das Verspotten eines wesentlichen Teils der Kerndomäne kann zu einigen Problemen führen, insbesondere wenn Sie das Programm ausführen und sich die Funktion zum Verspotten und Zusammenführen im geringsten Detail unterscheiden. Ein solcher Ansatz sollte für Fälle verwendet werden, in denen Sie mit externen Ressourcen arbeiten, z. B. wenn die zu sortierende Liste von dort stammt.
Cllamach