Ich bin ziemlich neu in TDD und ich habe Probleme, wenn ich meinen ersten Test erstelle, wenn er vor dem Implementierungscode steht. Ohne irgendeinen Rahmen für den Implementierungscode kann ich meinen ersten Test schreiben, wie ich will, aber er scheint immer von meiner Java / OO-Denkweise über das Problem befallen zu sein.
Zum Beispiel habe ich in meinem Github ConwaysGameOfLifeExample den ersten Test geschrieben (rule1_zeroNeighbours), indem ich ein GameOfLife-Objekt erstellt habe, das noch nicht implementiert wurde. bezeichnet eine nicht existierende Set-Methode, eine nicht existierende Step-Methode, eine nicht existierende Get-Methode und verwendet dann eine Zusicherung.
Die Tests entwickelten sich, als ich mehr Tests schrieb und überarbeitete, aber ursprünglich sah es ungefähr so aus:
@Test
public void rule1_zeroNeighbours()
{
GameOfLife gameOfLife = new GameOfLife();
gameOfLife.set(1, 1, true);
gameOfLife.step();
assertEquals(false, gameOfLife.get(1, 1));
}
Dies fühlte sich seltsam an, als ich das Design der Implementierung erzwang, basierend darauf, wie ich mich zu diesem frühen Zeitpunkt entschlossen hatte, diesen ersten Test zu schreiben.
Wie Sie TDD verstehen, ist das in Ordnung? Ich scheine den TDD / XP-Grundsätzen zu folgen, da sich meine Tests und Implementierung im Laufe der Zeit mit dem Refactoring weiterentwickelt haben. Wenn sich dieses anfängliche Design als unbrauchbar erwiesen hätte, wäre es offen für Änderungen gewesen, aber es scheint, als würde ich eine Richtung auf dem Weg erzwingen Lösung, indem Sie auf diese Weise starten.
Wie nutzen andere Menschen TDD? Ich hätte mehr überarbeiten können, indem ich ohne GameOfLife-Objekt angefangen hätte, nur mit primitiven und statischen Methoden, aber das scheint mir zu kompliziert.
quelle
Antworten:
Ich denke, das ist der entscheidende Punkt in Ihrer Frage: Ob dies wünschenswert ist oder nicht, hängt davon ab, ob Sie sich an Codeninjas Idee orientieren, dass Sie im Voraus entwerfen und dann TDD verwenden, um die Implementierung auszufüllen, oder an Durrons Idee, dass Tests beteiligt sein sollten das Design sowie die Implementierung zu vertreiben.
Ich denke, welche von diesen bevorzugen Sie (oder wo Sie in die Mitte fallen), ist etwas, das Sie als eine Präferenz für sich entdecken müssen. Es ist nützlich, die Vor- und Nachteile jedes Ansatzes zu verstehen. Es gibt wahrscheinlich viele, aber ich würde sagen, die wichtigsten sind:
Pro Upfront Design
Pro Test-Driving Design
Wenn Sie Ihre Implementierung auf einem Client Ihres Codes (Ihren Tests) aufbauen, erhalten Sie die YAGNI-Konformität so gut wie kostenlos, solange Sie nicht damit beginnen, nicht benötigte Testfälle zu schreiben. Ganz allgemein erhalten Sie eine API, die auf die Verwendung durch einen Verbraucher zugeschnitten ist und letztendlich Ihren Wünschen entspricht.
Die Idee, eine Reihe von UML-Diagrammen zu zeichnen, bevor Code geschrieben wird, und dann nur die Lücken auszufüllen, ist nett, aber selten realistisch. In Steve McConnells Code Complete wird Design bekanntlich als "böses Problem" beschrieben - ein Problem, das Sie nicht vollständig verstehen können, ohne es zuerst zumindest teilweise zu lösen. In Verbindung mit der Tatsache, dass sich das zugrunde liegende Problem durch sich ändernde Anforderungen ändern kann, fühlt sich dieses Designmodell ein bisschen hoffnungslos an. Mit dem Probefahren können Sie immer nur einen Teil der Arbeit abbeißen - nicht nur die Implementierung - und wissen, dass diese Aufgabe zumindest für die Lebensdauer der Umwandlung von Rot in Grün immer noch aktuell und relevant ist.
Was Ihr spezielles Beispiel betrifft, würden Sie, wie Durron sagt, mit einer einfacheren Schnittstelle als der in Ihrem Code-Snippet beginnen, wenn Sie den Entwurf durch Schreiben des einfachsten Tests vertreiben und dabei die minimale Schnittstelle nutzen würden, die es kann .
quelle
Um den Test überhaupt schreiben zu können, müssen Sie die API entwerfen, die Sie dann implementieren. Sie haben bereits auf dem falschen Fuß angefangen, indem Sie Ihren Test geschrieben haben, um das gesamte
GameOfLife
Objekt zu erstellen, und diesen zur Implementierung Ihres Tests verwendet haben.Aus dem praktischen Unit-Testing mit JUnit und Mockito :
Ihr Test macht keinen großen Versuch, eine API zu entwerfen. Sie haben ein Stateful-System eingerichtet, in dem die gesamte Funktionalität in der äußeren
GameOfLife
Klasse enthalten ist.Wenn ich diese Anwendung schreiben würde, würde ich stattdessen über die Teile nachdenken, die ich bauen möchte. Zum Beispiel könnte ich eine
Cell
Klasse machen, Tests dafür schreiben, bevor ich zur größeren Anwendung übergehe. Ich würde mit Sicherheit eine Klasse für die Datenstruktur "unendlich in jede Richtung" erstellen, die für die ordnungsgemäße Implementierung von Conway erforderlich ist, und das testen. Sobald das alles erledigt ist, würde ich darüber nachdenken, die Gesamtklasse zu schreiben, die einemain
Methode und so weiter hat.Es ist einfach, den Schritt "Schreiben eines Tests, der fehlschlägt" zu beschönigen. Den fehlerhaften Test so zu schreiben , wie Sie es möchten, ist der Kern von TDD.
quelle
boolean
, ist dieses Design mit Sicherheit für die Leistung schlechter. Es sei denn, es muss zukünftig auf andere zellulare Automaten mit mehr als zwei Zuständen erweiterbar sein.Es gibt unterschiedliche Denkrichtungen darüber.
Einige sagen: Nicht kompilierter Test ist ein Fehler - behebe das Problem und schreibe den kleinsten verfügbaren Produktionscode.
Einige sagen: Es ist in Ordnung, zuerst einen Test zu schreiben und dann zu prüfen, ob es scheiße ist (oder nicht) und fehlende Klassen / Methoden zu erstellen
Mit dem ersten Ansatz befinden Sie sich wirklich in einem Rot-Grün-Refaktor-Zyklus. Mit second haben Sie einen etwas breiteren Überblick, was Sie erreichen möchten.
Es liegt an Ihnen zu entscheiden, wie Sie arbeiten. IMHO sind beide Ansätze gültig.
quelle
Selbst wenn ich etwas auf eine "Hack it together" Weise implementiere, denke ich mir immer noch die Klassen und Schritte aus, die am gesamten Programm beteiligt sein werden. Sie haben dies also durchdacht und diese Design-Gedanken zuerst als Test niedergeschrieben - das ist großartig!
Durchlaufen Sie nun beide Implementierungen, um diesen ersten Test durchzuführen, und fügen Sie dann weitere Tests hinzu, um das Design zu verbessern und zu erweitern.
Was Ihnen helfen könnte, ist die Verwendung von Gurke oder Ähnlichem , um Ihre Tests zu schreiben.
quelle
Bevor Sie mit dem Schreiben Ihrer Tests beginnen, sollten Sie sich überlegen, wie Sie Ihr System entwerfen. Während der Entwurfsphase sollten Sie einige Zeit aufwenden. Wenn Sie es getan haben, werden Sie diese Verwirrung über TDD nicht bekommen.
TDD ist nur ein Link zum Entwicklungsansatz : TDD
1. Fügen Sie einen Test hinzu.
2. Führen Sie alle Tests aus und prüfen Sie, ob der neue fehlschlägt.
3. Schreiben Sie einen Code.
4. Führen Sie Tests aus.
5. Refactor-Code.
6. Wiederholen
Mit TDD können Sie alle erforderlichen Funktionen abdecken, die Sie geplant haben, bevor Sie mit der Entwicklung Ihrer Software beginnen. link: Vorteile
quelle
Ich weiß nicht wie System Level Tests geschrieben in Java oder C # aus diesem Grunde. In SpecFlow finden Sie C # oder eines der Cucumber-basierten Test-Frameworks für Java (möglicherweise JBehave). Dann können Ihre Tests eher so aussehen.
Und Sie können Ihr Objektdesign ändern, ohne alle Ihre Systemtests ändern zu müssen.
("Normale" Komponententests eignen sich hervorragend zum Testen einzelner Klassen.)
Was sind die Unterschiede zwischen BDD-Frameworks für Java?
quelle