Was ist die beste Vorgehensweise in TDD, wenn der Test nach korrekter Implementierung immer noch fehlschlägt (weil ein Fehler im Test vorliegt)?
Angenommen, Sie möchten die folgende Funktion entwickeln:
int add(int a, int b) {
return a + b;
}
Angenommen, wir entwickeln es in den folgenden Schritten:
Schreibtest (noch keine Funktion):
// test1 Assert.assertEquals(5, add(2, 3));
Ergibt einen Kompilierungsfehler.
Schreiben Sie eine Dummy-Funktionsimplementierung:
int add(int a, int b) { return 5; }
Ergebnis: bestanden
test1
.Fügen Sie einen weiteren Testfall hinzu:
// test2 -- notice the wrong expected value (should be 11)! Assert.assertEquals(12, add(5, 6));
Ergebnis:
test2
nicht bestanden,test1
noch bestanden.Schreiben Sie eine echte Implementierung:
int add(int a, int b) { return a + b; }
Ergebnis:
test1
noch bestanden,test2
noch nicht bestanden (seit11 != 12
).
In diesem speziellen Fall: Wäre es besser:
- richtig
test2
, und sehen, dass es jetzt passiert, oder - Löschen Sie den neuen Teil der Implementierung (dh gehen Sie zurück zu Schritt 2 oben), korrigieren
test2
Sie ihn und lassen Sie ihn fehlschlagen. Führen Sie dann die korrekte Implementierung wieder ein (Schritt 4 oben).
Oder gibt es einen anderen, klügeren Weg?
Obwohl ich verstehe, dass das Beispielproblem eher trivial ist, interessiert mich, was in dem generischen Fall zu tun ist, der komplexer sein könnte als die Addition von zwei Zahlen.
BEARBEITEN (als Antwort auf die Antwort von @Thomas Junk):
Der Schwerpunkt dieser Frage liegt auf dem, was TDD in einem solchen Fall vorschlägt, und nicht auf der "Universal Best Practice", um guten Code oder Tests zu erzielen (die sich möglicherweise von der TDD-Methode unterscheiden).
Antworten:
Das absolut Entscheidende ist, dass der Test sowohl erfolgreich als auch nicht erfolgreich ist.
Ob Sie den Code löschen, damit der Test fehlschlägt, und ihn dann neu schreiben oder in die Zwischenablage verschieben, um ihn später wieder einzufügen, spielt keine Rolle. TDD hat nie gesagt, dass Sie etwas neu eingeben müssen. Es möchte wissen, dass der Test nur dann bestanden wird, wenn er bestanden werden soll, und nur dann, wenn er nicht bestanden werden soll.
Wenn Sie sehen, ob der Test bestanden wurde oder nicht, testen Sie den Test. Vertrauen Sie niemals einem Test, den Sie noch nie gesehen haben.
Refactoring gegen die rote Leiste enthält formale Schritte zum Refactoring eines Funktionstests:
Wir überarbeiten jedoch keinen funktionierenden Test. Wir müssen einen Buggy-Test umbauen. Ein Problem ist Code, der eingeführt wurde, während nur dieser Test es abdeckte. Ein solcher Code sollte zurückgesetzt und wieder eingeführt werden, sobald der Test behoben ist.
Wenn dies nicht der Fall ist und die Codeabdeckung aufgrund anderer den Code abdeckender Tests kein Problem darstellt, können Sie den Test transformieren und als grünen Test einführen.
Hier wird auch Code zurückgesetzt, aber gerade genug, um den Test fehlschlagen zu lassen. Wenn das nicht ausreicht, um den gesamten eingeführten Code abzudecken, während nur der Buggy-Test abdeckt, brauchen wir ein größeres Code-Rollback und mehr Tests.
Führen Sie einen Grüntest ein
Das Aufbrechen des Codes kann darin bestehen, Code auskommentieren oder an eine andere Stelle zu verschieben, um ihn später wieder einzufügen. Dies zeigt uns den Umfang des Codes, den der Test abdeckt.
Bei den letzten beiden Läufen kehren Sie direkt in den normalen Rot-Grün-Zyklus zurück. Sie müssen nur etwas einfügen, anstatt etwas zu tippen, um den Code freizugeben und den Test zu bestehen. Stellen Sie also sicher, dass Sie nur so viel einfügen, dass der Test bestanden wird.
Das Gesamtmuster ist hier, um zu sehen, wie die Farbe des Tests die Art und Weise ändert, wie wir es erwarten. Beachten Sie, dass dies zu einer Situation führt, in der Sie kurzzeitig einen nicht vertrauenswürdigen Grüntest durchführen. Achten Sie darauf, dass Sie nicht unterbrochen werden und vergessen, wo Sie sich gerade befinden.
Mein Dank geht an RubberDuck für den Link Embracing the Red Bar .
quelle
Was ist das Gesamtziel , wollen Sie erreichen?
Schöne Tests machen?
Herstellung der korrekten Umsetzung?
TTD religiös richtig machen ?
Nichts des oben Genannten?
Vielleicht überdenken Sie Ihre Beziehung zu Tests und zum Testen.
Tests geben keine Gewähr für die Richtigkeit einer Implementierung. Wenn alle Tests bestanden sind, sagt dies nichts darüber aus, ob Ihre Software das tut, was sie sollte. Es gibt keine wesentlichen Aussagen über Ihre Software.
Nehmen Sie Ihr Beispiel:
Die "korrekte" Implementierung des Zusatzes wäre der Code, der äquivalent zu ist
a+b
. Und solange Ihr Code das tut , würden Sie sagen, dass der Algorithmus in seiner Funktion korrekt ist und korrekt implementiert ist.Auf dem ersten Blick , wir beide würden zustimmen, dass dies ist die Umsetzung einer Ergänzung.
Aber was wir tun , ist wirklich nicht zu sagen, dass dieser Code ist die Umsetzung die
addition
es nur verhält sich bis zu einem gewissen Grad , wie man: Man denke an Integer - Überlauf .Ganzzahliger Überlauf tritt im Code auf, aber nicht im Konzept von
addition
. Also: Ihr Code verhält sich bis zu einem gewissen Grad wie das Konzept vonaddition
, ist es aber nichtaddition
.Dieser eher philosophische Standpunkt hat mehrere Konsequenzen.
Und man kann sagen, dass Tests nichts anderes sind als Annahmen über das erwartete Verhalten Ihres Codes. Beim Testen Sie den Code, können Sie (vielleicht) nie sicher machen, Ihre Implementierung ist richtig , das Beste , was Sie sagen kann , ist, dass Ihre Erwartungen an , welche Ergebnisse der Code waren liefert oder nicht erfüllt; Sei es, dass dein Code falsch ist, sei es, dass dein Test falsch ist oder sei es, dass beide falsch sind.
Mithilfe nützlicher Tests können Sie Ihre Erwartungen an die Funktionsweise des Codes festlegen : Solange ich meine Erwartungen nicht ändere und der geänderte Code das erwartete Ergebnis liefert, kann ich mir sicher sein, dass die von mir getroffenen Annahmen zutreffen Die Ergebnisse scheinen zu klappen.
Das hilft nicht, wenn Sie die falschen Annahmen getroffen haben. aber hey! Zumindest beugt es Schizophrenie vor: Erwarten Sie unterschiedliche Ergebnisse, wenn es keine geben sollte.
tl; dr
Ihre Tests sind Annahmen über das Verhalten des Codes. Wenn Sie guten Grund zur Annahme haben, dass Ihre Implementierung richtig ist, korrigieren Sie den Test und prüfen Sie, ob diese Annahme zutrifft.
quelle
datatype
ist das eindeutig die falsche Wahl. Ein Test würde ergeben: Ihre Erwartung wäre »funktioniert für große Zahlen« und wird in einigen Fällen nicht erfüllt. Dann wäre die Frage, wie mit diesen Fällen umgegangen werden soll. Sind sie Eckfälle? Wann ja, wie soll man damit umgehen? Vielleicht helfen einige Quard-Klauseln, ein größeres Durcheinander zu verhindern. Die Antwort ist kontextgebunden.Sie müssen wissen, dass der Test fehlschlagen wird, wenn die Implementierung falsch ist. Dies ist nicht dasselbe wie das Bestehen, wenn die Implementierung richtig ist. Daher sollten Sie den Code vor dem Korrigieren des Tests wieder in einen Zustand versetzen, in dem Sie einen Fehler erwarten , und sicherstellen, dass der Fehler aus dem von Ihnen erwarteten Grund (z. B.
5 != 12
) und nicht aus einem anderen, von Ihnen nicht vorhergesagten Grund auftritt.quelle
assertTrue(5 == add(2, 3))
eine weniger nützliche Ausgabe, alsassertEqual(5, add(2, 3))
wenn beide dasselbe testen).In diesem speziellen Fall, wenn Sie die 12 in eine 11 ändern und der Test nun erfolgreich ist, haben Sie den Test und die Implementierung meiner Meinung nach gut getestet. Es ist also nicht viel erforderlich, zusätzliche Rahmen zu durchlaufen.
Das gleiche Problem kann jedoch in komplexeren Situationen auftreten, z. B. wenn Sie einen Fehler in Ihrem Setup-Code haben. In diesem Fall sollten Sie nach dem Korrigieren des Tests wahrscheinlich versuchen, die Implementierung so zu ändern, dass der betreffende Test fehlschlägt, und dann die Änderung rückgängig machen. Wenn das Zurücksetzen der Implementierung der einfachste Weg ist, dann ist das in Ordnung. In Ihrem Beispiel könnten Sie
a + b
zua + a
oder mutierena * b
.Wenn Sie alternativ die Behauptung leicht ändern und feststellen, dass der Test fehlschlägt, kann dies beim Testen des Tests sehr effektiv sein.
quelle
Ich würde sagen, dies ist ein Fall für Ihr Lieblingsversionskontrollsystem:
Führen Sie die Korrektur des Tests durch und behalten Sie Ihre Codeänderungen in Ihrem Arbeitsverzeichnis bei.
Bestätigen Sie mit einer entsprechenden Nachricht
Fixed test ... to expect correct output
.Mit
git
könnte diese Anwendung benötigen ,git add -p
wenn der Test und Implementierung ist in der gleichen Datei, sonst kann man natürlich die Bühne einfach die beiden Dateien getrennt.Übernehmen Sie den Implementierungscode.
Gehen Sie in der Zeit zurück, um die in Schritt 1 vorgenommene Festschreibung zu testen, und stellen Sie sicher, dass der Test tatsächlich fehlschlägt .
Sie sehen, auf diese Weise verlassen Sie sich nicht auf Ihre Bearbeitungsfähigkeiten, um Ihren Implementierungscode aus dem Weg zu räumen, während Sie Ihren fehlgeschlagenen Test testen. Sie setzen Ihr VCS ein, um Ihre Arbeit zu speichern und sicherzustellen, dass der korrekt aufgezeichnete VCS-Verlauf sowohl den fehlerhaften als auch den bestandenen Test enthält.
quelle