Warum nicht alle Tests auf einmal schreiben, wenn Sie TDD machen?

54

Der Rot-Grün-Refaktor-Zyklus für TDD ist gut etabliert und akzeptiert. Wir schreiben einen nicht bestandenen Komponententest und lassen ihn so einfach wie möglich bestehen. Was sind die Vorteile dieses Ansatzes gegenüber dem Schreiben vieler fehlgeschlagener Komponententests für eine Klasse, die alle auf einmal bestehen.

Die Testsuite schützt Sie weiterhin vor dem Schreiben von falschem Code oder vor Fehlern in der Umgestaltungsphase. Was ist also der Schaden? Manchmal ist es einfacher, zuerst alle Tests für eine Klasse (oder ein Modul) als eine Form von "Brain Dump" zu schreiben, um schnell das gesamte erwartete Verhalten auf einmal aufzuschreiben.

RichK
quelle
20
Tun Sie, was für Sie am besten funktioniert (nach einigem Experimentieren). Dem Dogma blind zu folgen ist niemals eine gute Sache.
Michael Borgwardt
6
Ich gehe davon aus, dass das gleichzeitige Schreiben aller Tests dem gleichzeitigen Schreiben des gesamten App-Codes ähnelt .
Michael Haren
1
@MichaelHaren Alle Tests für eine Klasse (oder ein Funktionsmodul), sorry für die Verwirrung
RichK
3
Bewältigung des Problems "Brain Dump": Manchmal gibt es Punkte beim Testen / Codieren, wenn Sie feststellen, dass mehrere verschiedene spezifische Eingabetests erforderlich sind, und es besteht die Tendenz, die Klarheit dieser Erkenntnis zu nutzen, bevor Sie sich von dem ablenken lassen Minutien der Kodierung. Normalerweise schaffe ich das, indem ich eine separate Liste (z. B. Mylyn) führe oder eine Liste mit Kommentaren in der Test-Klasse mit verschiedenen Dingen, an die ich mich erinnern möchte (z. B. // test null case). Ich codiere jedoch immer noch nur einen Test gleichzeitig und arbeite mich stattdessen systematisch durch die Liste.
Sam Goldberg
1
Nun, ich weiß nicht, warum das niemand erwähnt hat, aber Sie können nicht alle Tests auf einmal schreiben. Das Schreiben aller Tests vor der Hand entspricht genau dem Schreiben von BDUF . Und was hat uns die Geschichte über BDUF gelehrt? Es funktioniert fast nie.
Songo

Antworten:

49

Beim testgetriebenen Design geht es darum, die API richtig zu machen, nicht den Code.

Der Vorteil, die einfachsten fehlgeschlagenen Tests zuerst zu schreiben, besteht darin, dass Sie Ihre API (die Sie im Wesentlichen im laufenden Betrieb entwerfen) so einfach wie möglich erhalten. Vorne.

Alle zukünftigen Verwendungen (für die Sie als Nächstes Tests schreiben) basieren auf dem anfänglichen einfachen Design und nicht auf einem suboptimalen Design, das komplexere Fälle bewältigt.

user1249
quelle
Hervorragende Punkte! Wir sind manchmal so sehr mit dem Testen des Codes beschäftigt, dass wir manchmal ignorieren, wie wichtig die API und das Domänenmodell sein können, bevor wir überhaupt Ihren ersten Test schreiben.
maple_shaft
+1 für die tatsächliche Umsetzung der Absicht von Test Driven Development.
Joshua Drake
76

Wenn Sie einen Test schreiben , konzentrieren Sie sich auf eine Sache.
Bei vielen Tests konzentrieren Sie sich auf viele Aufgaben, daher ist dies keine gute Idee.

Abyx
quelle
8
Wer würde das ablehnen ?!
CaffGeek
6
@Chad Ich war nicht der Abgestimmte, aber ich glaube diese Antwort verfehlt das Offensichtliche. Bei der testgesteuerten Entwicklung geht es darum, mithilfe der Tests das Design des Codes zu steuern. Sie schreiben den Test individuell, um das Design weiterzuentwickeln, nicht nur aus Gründen der Testbarkeit. Wenn es nur um die Testartefakte ginge, dann wäre dies eine gute Antwort, aber wie es ist, fehlen einige wichtige Informationen.
Joshua Drake
7
Ich habe das nicht abgelehnt, aber; Ich habe darüber nachgedacht. Die Antwort auf eine komplexe Frage ist viel zu kurz.
Mark Weston
2
+1 für die Konzentration auf eine Sache zu einem Zeitpunkt, ist unsere Fähigkeit zum Multitasking überbewertet.
CCTAN
Es ist die einfachste Antwort, die funktionieren könnte.
DNA
26

Eine der Schwierigkeiten beim Schreiben von Komponententests besteht darin, dass Sie Code schreiben, der an sich möglicherweise fehleranfällig ist. Es besteht auch die Möglichkeit, dass Sie Ihre Tests später aufgrund eines Umgestaltungsaufwands ändern müssen, während Sie Ihren Implementierungscode schreiben. Mit TDD bedeutet dies, dass Sie möglicherweise ein wenig von Ihren Tests mitgerissen werden und feststellen müssen, dass Sie viele im Wesentlichen "ungetestete" Testcodes neu schreiben müssen, wenn Ihre Implementierung im Verlauf des Projekts ausgereift ist. Ein Weg, um diese Art von Problem zu vermeiden, besteht darin, sich einfach darauf zu konzentrieren, jeweils nur eine einzige Sache zu tun. Dies stellt sicher, dass Sie die Auswirkungen von Änderungen auf Ihre Tests minimieren.

Natürlich hängt dies hauptsächlich davon ab, wie Sie Ihren Testcode schreiben. Schreiben Sie einen Komponententest für jede einzelne Methode oder schreiben Sie Tests, die sich auf Funktionen / Anforderungen / Verhaltensweisen konzentrieren? Ein anderer Ansatz könnte darin bestehen, einen verhaltensorientierten Ansatz mit einem geeigneten Framework zu verwenden und sich darauf zu konzentrieren, Tests so zu schreiben, als wären sie Spezifikationen. Dies würde bedeuten, entweder die BDD-Methode zu übernehmen oder BDD-Tests anzupassen, wenn Sie sich formeller an TDD halten möchten. Alternativ können Sie sich ganz an das TDD-Paradigma halten, die Art und Weise, in der Sie Tests schreiben, jedoch ändern, sodass Sie sich nicht ausschließlich auf einzelne Testmethoden konzentrieren, sondern das Verhalten allgemeiner testen, um die Besonderheiten der von Ihnen implementierten Anforderungsmerkmale zu erfüllen.

Unabhängig davon, welchen spezifischen Ansatz Sie wählen, verwenden Sie in allen oben beschriebenen Fällen einen Test-First-Ansatz. Es ist also möglicherweise verlockend, Ihr Gehirn einfach in eine reizende Testsuite herunterzuladen, aber Sie möchten auch gegen den vorgehen Versuchung, mehr zu tun, als unbedingt notwendig ist. Immer wenn ich eine neue Testsuite starten möchte, beginne ich, YAGNI für mich selbst zu wiederholen, und füge es manchmal sogar in einen Kommentar in meinem Code ein, um mich daran zu erinnern, dass ich mich auf das konzentrieren soll, was sofort wichtig ist, und nur das Minimum zu tun, um die Anforderungen zu erfüllen Anforderungen des Features, das ich implementieren möchte. Das Festhalten an Red-Green-Refactor hilft dabei, dies sicherzustellen.

S.Robins
quelle
Ich freue mich, dass Sie auf den Unterschied hingewiesen haben, wie man den Testcode schreibt. Einige schreiben gerne einen einzelnen Master-Unit-Test, der alle realistischen Möglichkeiten der Eingabe für eine einzelne Funktion oder Methode abdeckt. Andere verfolgen mit ihren Unit-Tests einen eher BDD-Ansatz. Diese Unterscheidung ist wichtig, wenn festgestellt werden soll, ob das Schreiben einer ganzen Testsuite wichtig ist oder nicht. Toller Einblick!
maple_shaft
17

Ich denke, wenn Sie dies tun, verpassen Sie den TDD- Prozess . Wenn Sie zu Beginn nur all Ihre Tests schreiben, werden Sie den Entwicklungsprozess mit TDD nicht wirklich durchlaufen. Sie raten einfach vorher, welche Tests Sie benötigen. Dies sind ganz andere Tests als die, die Sie schreiben, wenn Sie sie bei der Entwicklung Ihres Codes einzeln durchführen. (Es sei denn, Ihr Programm ist trivialer Natur.)

ZweiBlumen
quelle
1
Die meisten Geschäfts- und Unternehmensanwendungen sind technisch trivialer Natur. Da die meisten Anwendungen geschäftlich und unternehmerisch sind, sind die meisten Anwendungen auch von Natur aus trivial.
maple_shaft
5
@maple_shaft - die Technologie mag trivial sein, die Geschäftsregeln jedoch nicht. Versuchen Sie, eine App für 5 Manager zu erstellen, die unterschiedliche Anforderungen haben und sich weigern, etwas BS über Ihr simples, elegantes, weniger-ist-mehr-Minimalismus-Design anzuhören.
JeffO
5
@ JeffO 1) Es ist nicht BS. 2) Ein elegantes minimalistisches Design erfordert gute Fähigkeiten in der Softwareentwicklung. 3) Die Fähigkeit, die Anforderungen von 5 verschiedenen Managern zu mindern, die nicht mehr als 5 Minuten pro Woche Zeit haben, um mit Ihnen zu verschwenden und dennoch ein minimalistisches Design zu entwickeln, erfordert einen ausgezeichneten Softwareentwickler. Pro-Tipp: Software-Entwicklung ist mehr als nur Programmierkenntnisse, es ist Verhandeln, Konversation und Übernahme von Verantwortung. Du musst Alpha-Hund sein und manchmal zurückbeißen.
maple_shaft
1
Wenn ich richtig verstehe, wirft diese Antwort die Frage auf.
Konrad Rudolph
1
@maple_shaft Ich denke, das war es, worauf Jeff O mit seinem Kommentar abzielte, nein?
ZweiBlumen
10

Ich "schreibe" alle Tests, die mir im Vorfeld beim "Brainstorming" einfallen, aber ich schreibe jeden Test als einen Kommentar , der den Test beschreibt.

Ich konvertiere dann einen Test in Code und erledige die Arbeit, damit er kompiliert und bestanden wird . Oft entscheide ich mich, dass ich nicht alle Tests benötige, von denen ich dachte, dass ich sie durchgeführt habe, oder dass ich andere Tests benötige. Diese Informationen stammen nur aus dem Schreiben des Codes, damit die Tests bestanden werden.

Das Problem ist, dass Sie einen Test erst dann in Code schreiben können, wenn Sie die Methode erstellt und die Klassen definiert haben, die er testet. Andernfalls werden nur viele Compilerfehler angezeigt, die die Art und Weise beeinflussen, wie Sie jeweils an einem einzelnen Test arbeiten.

Wenn Sie jetzt ein System wie spec flow verwenden, wenn die Tests in „Englisch“ geschrieben sind, möchten Sie die Kunden möglicherweise dazu bringen, einer Reihe von Tests zuzustimmen, während Sie Zeit haben, anstatt nur einen einzelnen Test zu erstellen.

Ian
quelle
1
Ja, obwohl ich mit den obigen Antworten einverstanden bin, die auf die Probleme bei der Codierung aller Ihrer Tests hinweisen, finde ich es sehr hilfreich, ein Gesamtverständnis darüber zu gewinnen, wie sich die aktuelle Methode als Satz von Testbeschreibungen ohne Code verhalten sollte. Durch das Aufschreiben dieser Informationen wird in der Regel geklärt, ob ich die Anforderungen an den zu schreibenden Code vollständig verstanden habe und ob es Randfälle gibt, an die ich nicht gedacht habe. Ich finde es viel bequemer, den ersten Test zu codieren und ihn dann zu bestehen, nachdem ich meine "Übersicht" darüber gegeben habe, wie die Methode funktionieren sollte.
Mark Weston
10

Die Idee hinter TDD sind schnelle Iterationen.

Wenn Sie eine große Menge von Tests haben, die geschrieben werden müssen, bevor Sie Ihren Code schreiben müssen, ist es schwierig, Ihren Code iterativ umzugestalten.

Ohne einfaches Code-Refactoring verlieren Sie viele Vorteile von TDD.

Linkerro
quelle
5

In meiner (begrenzten) Erfahrung mit TDD kann ich Ihnen sagen, dass jedes Mal , wenn ich die Disziplin gebrochen habe, einen Test nach dem anderen zu schreiben, die Dinge schlecht gelaufen sind. Es ist eine einfache Falle, in die man hineinfallen kann. "Oh, diese Methode ist trivial", überlegen Sie, "also schalte ich diese beiden anderen verwandten Tests einfach aus und bewege mich weiter." Rate mal? Nichts ist so trivial wie es scheint. Jedes Mal, wenn ich in diese Falle geriet, debuggte ich etwas, das ich für einfach hielt, aber es stellte sich heraus, dass es seltsame Eckfälle gab. Und da ich mehrere Tests gleichzeitig geschrieben hatte, war es eine Menge Arbeit, herauszufinden, wo der Fehler war.

Wenn Sie einen Brain Dump mit Informationen benötigen, haben Sie viele Möglichkeiten:

  • Whiteboard
  • Benutzergeschichten
  • Bemerkungen
  • Gute alte Feder und Papier

Beachten Sie, dass sich der Compiler nirgends auf dieser Liste befindet. :-)

Kristo
quelle
5

Sie setzen voraus, dass Sie wissen, wie Ihr Code aussehen wird, bevor Sie ihn schreiben. TDD / BDD ist sowohl ein Design- / Discovery-Prozess als auch ein QS-Prozess. Für ein bestimmtes Feature schreiben Sie den einfachsten Test, mit dem überprüft werden kann, ob das Feature erfüllt ist (manchmal sind aufgrund der Komplexität eines Features mehrere erforderlich). Der erste Test, den Sie schreiben, enthält Annahmen darüber, wie der Arbeitscode aussehen wird. Wenn Sie die gesamte Testsuite schreiben, bevor Sie die erste Codezeile schreiben, um sie zu unterstützen, erstellen Sie eine Litanei nicht überprüfter Annahmen. Schreiben Sie stattdessen eine Annahme und überprüfen Sie sie. Dann schreib den nächsten. Bei der Überprüfung der nächsten Annahme brechen Sie möglicherweise eine frühere Annahme, sodass Sie zurückgehen und entweder diese erste Annahme ändern müssen, um sie an die Realität anzupassen, oder die Realität ändern müssen, damit die erste Annahme weiterhin gültig ist.

Stellen Sie sich jeden Einheitentest vor, den Sie als Theorie in ein wissenschaftliches Notizbuch schreiben. Wenn Sie das Notizbuch ausfüllen, beweisen Sie Ihre Theorien und bilden neue. Manchmal widerlegt das Beweisen einer neuen Theorie eine vorherige Theorie, sodass Sie sie korrigieren müssen. Es ist einfacher, eine Theorie nach der anderen zu beweisen, als zu versuchen, 20 auf einmal zu beweisen.

Michael Brown
quelle
TDD geht davon aus, dass Sie wissen, wie Ihr Code aussehen wird, bevor Sie ihn auch schreiben, nur in kleineren Teilen.
Michael Shaw
4

TDD ist ein hochgradig iterativer Ansatz, der meiner Erfahrung nach besser zu den realen Entwicklungsmethoden passt. Normalerweise nimmt meine Implementierung während dieses Prozesses allmählich Gestalt an, und jeder Schritt kann weitere Fragen, Erkenntnisse und Ideen zum Testen bringen. Dies ist ideal, um mich auf die eigentliche Aufgabe zu konzentrieren, und sehr effizient, da ich zu jedem Zeitpunkt nur eine begrenzte Anzahl von Dingen im Kurzzeitgedächtnis behalten muss. Dies verringert wiederum die Möglichkeit von Fehlern.

Ihre Idee ist im Grunde ein Big Test Up Front-Ansatz, der meiner Meinung nach schwieriger zu handhaben ist und möglicherweise verschwenderischer wird. Was passiert, wenn Sie während Ihrer Arbeit feststellen, dass Ihr Ansatz nicht gut ist, Ihre API fehlerhaft ist und Sie neu beginnen müssen oder stattdessen eine Bibliothek eines Drittanbieters verwenden müssen? Dann wird ein Großteil der Arbeit, die Sie in das Schreiben Ihrer Tests investiert haben, zu einer Verschwendung von Aufwand.

Das heißt, wenn dies für Sie funktioniert, gut. Ich kann mir vorstellen, dass Sie, wenn Sie von einer festen, detaillierten technischen Spezifikation aus, auf einem Gebiet arbeiten, mit dem Sie vertraut sind, und / oder auf einer relativ kleinen Aufgabe, die meisten oder alle erforderlichen Testfälle bereit haben und Ihre Implementierung direkt ab der Beginn. Dann kann es sinnvoll sein, zunächst alle Tests auf einmal zu schreiben. Wenn Sie auf lange Sicht produktiver werden, müssen Sie sich nicht allzu viele Gedanken über Regelbücher machen :-)

Péter Török
quelle
4

Abgesehen davon, dass man nur an eine Sache denkt, besteht ein Paradigma von TDD darin, möglichst wenig Code zu schreiben, um den Test zu bestehen. Wenn Sie einen Test nach dem anderen schreiben, ist es viel einfacher, den Pfad zum Schreiben von gerade genug Code zu erkennen, um diesen Test zu bestehen. Mit einer ganzen Reihe von Tests kommen Sie nicht in kleinen Schritten zum Code, sondern müssen einen großen Sprung machen, um alle Tests auf einmal bestehen zu können.

Wenn Sie sich nicht darauf beschränken, den Code so zu schreiben, dass alle Tests auf einmal bestanden werden, sondern nur so viel Code schreiben, dass Sie jeweils einen Test bestehen, funktioniert dies möglicherweise noch. Sie müssten mehr Disziplin haben, um nicht nur mehr Code zu schreiben, als Sie benötigen. Sobald Sie diesen Weg eingeschlagen haben, können Sie mehr Code schreiben, als die beschriebenen Tests beschreiben. Dieser kann ungetestet sein , zumindest in dem Sinne, dass er nicht von einem Test gesteuert wird und möglicherweise in dem Sinne, dass er nicht benötigt wird (oder ausgeübt) durch irgendeinen Test.

Es ist durchaus akzeptabel, zu notieren, was die Methode tun soll, wie z. B. Kommentare, Geschichten, eine funktionale Spezifikation usw. Ich würde warten, um diese in Tests eins nach dem anderen zu übersetzen.

Das andere, was Sie übersehen können, wenn Sie die Tests auf einmal schreiben, ist der Denkprozess, bei dem Sie durch Bestehen eines Tests dazu veranlasst werden, über andere Testfälle nachzudenken. Ohne eine Reihe vorhandener Tests müssen Sie im Kontext des letzten bestandenen Tests an den nächsten Testfall denken. Wie ich bereits sagte, ist es sehr gut, eine gute Vorstellung davon zu haben, was die Methode bewirken soll, aber oft habe ich neue Möglichkeiten gefunden, die ich nicht a priori in Betracht gezogen hatte, die sich aber erst beim Schreiben der Methode ergaben Tests. Es besteht die Gefahr, dass Sie diese verpassen, es sei denn, Sie haben sich ausdrücklich angewöhnt, darüber nachzudenken, welche neuen Tests ich schreiben kann, die ich noch nicht habe.

Tvanfosson
quelle
3

Ich habe an einem Projekt gearbeitet, in dem Entwickler, die die (fehlgeschlagenen) Tests geschrieben haben, sich von den Entwicklern unterschieden, die den erforderlichen Code implementierten, damit sie bestanden, und ich fand es sehr effektiv.

In diesem Fall wurden nur die Tests, die sich auf die aktuelle Iteration beziehen, einmal geschrieben. Was Sie also vorschlagen, ist in einem solchen Szenario durchaus möglich.

user2567
quelle
2
  • Dann versuchst du, dich auf zu viele Dinge gleichzeitig zu konzentrieren.
  • Während der Implementierung, um alle Tests zu bestehen, ist keine funktionsfähige Version Ihrer Anwendung verfügbar. Wenn Sie viel implementieren müssen, haben Sie über einen langen Zeitraum keine funktionierende Version.
Magomi
quelle
2

Der Rot-Grün-Refaktor-Zyklus ist eine Checkliste für TDD-Neuentwickler. Ich würde sagen, es ist eine gute Idee, dieser Checkliste zu folgen, bis Sie wissen, wann sie zu befolgen ist und wann Sie sie brechen können (das heißt, bis Sie wissen, dass Sie diese Frage nicht bei stackoverflow stellen müssen :)

Nachdem ich fast ein Jahrzehnt lang TDD gemacht habe, kann ich Ihnen sagen, dass ich sehr selten, wenn überhaupt, viele fehlerhafte Tests schreibe, bevor ich Produktionscode schreibe.

Torbjörn Kalin
quelle
1

Sie beschreiben BDD, bei dem einige externe Stakeholder eine ausführbare Spezifikation haben. Dies kann von Vorteil sein, wenn eine vorgegebene Vorabspezifikation vorliegt (z. B. eine Formatspezifikation, ein Industriestandard oder wenn der Programmierer nicht der Domänenexperte ist).

Der normale Ansatz besteht dann darin, nach und nach immer mehr Abnahmetests abzudecken. Dies ist der Fortschritt, der für den Projektmanager und den Kunden sichtbar ist.

Normalerweise lassen Sie diese Tests in einem BDD-Framework wie Cucumber, Fitnesse oder einem anderen festlegen und ausführen.

Dies ist jedoch nichts, was Sie mit Ihren Komponententests verwechseln, da diese den Details der Implementierung viel näher kommen, da sich viele API-bezogene Randfälle, Initialisierungsprobleme usw. stark auf das zu testende Objekt konzentrieren , das ein Implementierungsartefakt ist .

Die Rot-Grün-Refaktor-Disziplin hat viele Vorteile, und der einzige Vorteil, auf den Sie hoffen können, wenn Sie sie im Voraus eingeben, ist die Gewinnschwelle.

Tormod
quelle
1

Ein Test zur Zeit: Der Hauptvorteil besteht darin, sich auf eine Sache zu konzentrieren. Denken Sie an Tiefen-First-Design: Sie können tief gehen und mit einer schnellen Feedback-Schleife konzentriert bleiben. Sie können jedoch den Umfang des gesamten Problems verpassen! Das ist der Moment, in dem (großes) Refactoring ins Spiel kommt. Ohne das funktioniert TDD nicht.

Alle Tests: Analyse und Entwurf zeigen möglicherweise mehr über den Umfang des Problems auf. Denken Sie an das Design mit der ersten Breite. Sie analysieren das Problem aus mehreren Blickwinkeln und fügen den Input aus der Erfahrung hinzu. Es ist von Natur aus schwieriger, kann aber interessante Vorteile bringen - weniger Refactoring - wenn Sie "gerade genug davon" tun. Beachten Sie, dass es leicht ist, zu viel zu analysieren und dennoch die Marke völlig zu verfehlen!

Es fällt mir schwer, allgemein zu empfehlen, das eine oder das andere zu bevorzugen, da es viele Faktoren gibt: Erfahrung (insbesondere mit demselben Problem), Fachkenntnisse und Fähigkeiten, Freundlichkeit des Codes für das Refactoring, Komplexität des Problems ...

Ich schätze, wenn wir uns stärker auf typische Geschäftsanwendungen konzentrieren, würde TDD mit seinem schnellen Versuchs- und Fehleransatz in der Regel hinsichtlich seiner Effektivität gewinnen.

Beschädigen
quelle
1

Unter der Annahme, dass Ihr Test-Framework dies unterstützt, würde ich vorschlagen, dass Sie anstelle der Implementierung der Tests, für die Sie einen Braindump durchführen möchten, beschreibende ausstehende Tests schreiben, die Sie später implementieren werden. Wenn Ihre API zum Beispiel foo und bar, aber nicht biz sein soll, fügen Sie einfach den folgenden Code (dieses Beispiel ist in rspec) für Ihre Testsuite hinzu und greifen Sie sie nacheinander an. Sie erhalten Ihre Gedanken schnell und können alle Ihre Probleme eins nach dem anderen ansprechen. Wenn alle Tests bestanden sind, wissen Sie, wann Sie alle Probleme behoben haben, die Sie während Ihres Braindumps hatten.

describe "Your API" do

  it "should foo" do
    pending "braindump from 4/2"
  end

  it "should bar" do
    pending "braindump from 4/2"
  end

  it "should not biz" do
    pending "braindump from 4/2"
  end

end
Lösegeld Briggs
quelle