Alle Beispiele, die ich in Schulungsvideos gelesen und gesehen habe, enthalten vereinfachende Beispiele. Aber was ich nicht sehe, ist, wie ich den "echten" Code mache, nachdem ich grün geworden bin. Ist das der "Refactor" Teil?
Wenn ich ein ziemlich komplexes Objekt mit einer komplexen Methode habe und meinen Test und das Nötigste schreibe, um ihn zu bestehen (nachdem er zum ersten Mal fehlgeschlagen ist, Rot). Wann gehe ich zurück und schreibe den richtigen Code? Und wie viel echten Code schreibe ich, bevor ich erneut teste? Ich vermute, dass letzteres mehr Intuition ist.
Edit: Danke an alle die geantwortet haben. Alle Ihre Antworten haben mir sehr geholfen. Es scheint unterschiedliche Vorstellungen darüber zu geben, was ich gefragt oder verwirrt habe, und vielleicht gibt es sie, aber ich habe gefragt, ob ich eine Bewerbung für den Bau einer Schule habe.
In meinem Design habe ich eine Architektur, mit der ich beginnen möchte, User Stories usw. Von hier aus nehme ich diese User Stories und erstelle einen Test, um die User Story zu testen. Der Benutzer sagt, wir haben Leute, die sich für die Schule anmelden und Registrierungsgebühren zahlen. Ich überlege, wie ich das zum Scheitern bringen kann. Dabei entwerfe ich eine Testklasse für die Klasse X (vielleicht Student), die fehlschlagen wird. Ich erstelle dann die Klasse "Student". Vielleicht "Schule" weiß ich nicht.
Aber auf jeden Fall zwingt mich das TD- Design , die Geschichte zu durchdenken. Wenn ein Test fehlschlägt, weiß ich, warum er fehlschlägt, aber dies setzt voraus, dass ich ihn bestehen kann. Es geht um das Entwerfen.
Ich vergleiche das mit dem Nachdenken über Rekursion. Rekursion ist kein schwieriges Konzept. Es mag schwieriger sein, den Überblick in Ihrem Kopf zu behalten, aber in Wirklichkeit ist es am schwierigsten zu wissen, wann die Rekursion "abbricht", wann man aufhört (meiner Meinung nach natürlich). Also muss ich darüber nachdenken, was aufhört die Rekursion zuerst. Es ist nur eine unvollständige Analogie und es wird angenommen, dass jede rekursive Iteration ein "Durchlauf" ist. Nochmals nur eine Meinung.
In der Umsetzung ist die Schule schwerer zu sehen. Numerische und Bankbücher sind "einfach" in dem Sinne, dass Sie einfache Arithmetik verwenden können. Ich kann a + b sehen und 0 zurückgeben usw. Bei einem System von Leuten muss ich genauer überlegen, wie man das umsetzt . Ich habe das Konzept von Fail, Pass, Refactor (hauptsächlich aufgrund des Studiums und dieser Frage).
Was ich nicht weiß, beruht meiner Meinung nach auf mangelnder Erfahrung. Ich weiß nicht, wie ich einen neuen Studenten nicht anmelden soll. Ich weiß nicht, wie ich jemanden daran hindern kann, einen Nachnamen einzugeben und ihn in einer Datenbank zu speichern. Ich weiß, wie man eine +1 für einfache Mathematik erstellt, aber bei Entitäten wie einer Person weiß ich nicht, ob ich nur prüfe, ob ich eine eindeutige Datenbank-ID oder etwas anderes zurückerhalte, wenn jemand einen Namen in a eingibt Datenbank oder beide oder keine.
Oder vielleicht zeigt dies, dass ich immer noch verwirrt bin.
Antworten:
Sie gehen nicht "zurück" und schreiben "echten Code". Es ist alles realer Code. Gehen Sie zurück und fügen Sie einen weiteren Test hinzu, der Sie dazu zwingt , Ihren Code zu ändern , damit der neue Test bestanden wird.
Wie viel Code schreiben Sie, bevor Sie erneut testen? Keiner. Sie schreiben Null - Code ohne Fehler Test, zwingt Sie mehr Code zu schreiben.
Beachten Sie das Muster?
Lassen Sie uns ein (weiteres) einfaches Beispiel durchgehen, in der Hoffnung, dass es hilft.
Einfach peazy.
Nicht das, was Sie als echten Code bezeichnen würden, oder? Fügen wir einen Test hinzu, der eine Änderung erzwingt.
Wir könnten so etwas albernes machen
if n == 1
, aber wir werden zur vernünftigen Lösung übergehen.Cool. Dies funktioniert für alle Nicht-FizzBuzz-Nummern. Was ist die nächste Eingabe, die eine Änderung des Produktionscodes erzwingt?
Und wieder. Schreiben Sie einen Test, der noch nicht bestanden wird.
Und wir haben jetzt alle Vielfachen von drei abgedeckt (das sind nicht auch Vielfachen von fünf, wir werden es notieren und zurückkommen).
Wir haben noch keinen Test für "Buzz" geschrieben, also schreiben wir das.
Und wieder wissen wir, dass es einen anderen Fall gibt, den wir behandeln müssen.
Und jetzt können wir alle Vielfachen von 5 behandeln, die nicht auch Vielfachen von 3 sind.
Bis zu diesem Punkt haben wir den Refactoring-Schritt ignoriert, aber ich sehe einige Überschneidungen. Lassen Sie uns das jetzt aufräumen.
Cool. Jetzt haben wir die Duplizierung entfernt und eine gut benannte Funktion erstellt. Was ist der nächste Test, den wir schreiben können, der uns zwingt, den Code zu ändern? Nun, wir haben den Fall vermieden, in dem die Zahl sowohl durch 3 als auch durch 5 teilbar ist. Schreiben wir es jetzt.
Die Tests bestehen, aber wir haben mehr Wiederholungen. Wir haben Optionen, aber ich werde "Lokale Variable extrahieren" einige Male anwenden, damit wir überarbeiten, anstatt neu zu schreiben.
Und wir haben jeden vernünftigen Input behandelt, aber was ist mit unvernünftigem Input? Was passiert, wenn wir 0 oder ein Negativ übergeben? Schreiben Sie diese Testfälle.
Sieht das jetzt schon nach "echtem Code" aus? Und was noch wichtiger ist: Ab wann war es kein "unwirklicher Code" mehr und der Übergang zum "echten" Code? Das ist etwas zum Nachdenken ...
Also konnte ich dies einfach tun, indem ich nach einem Test suchte, von dem ich wusste, dass er nicht bei jedem Schritt bestanden wird, aber ich hatte viel Übung. Wenn ich bei der Arbeit bin, sind die Dinge nicht immer so einfach und ich weiß möglicherweise nicht immer, welcher Test eine Änderung erzwingen wird. Manchmal schreibe ich einen Test und wundere mich, dass er bereits bestanden wurde! Ich empfehle dringend, dass Sie sich angewöhnen, eine "Testliste" zu erstellen, bevor Sie anfangen. Diese Testliste sollte alle "interessanten" Eingaben enthalten, die Sie sich vorstellen können. Möglicherweise verwenden Sie nicht alle und fügen im Laufe der Zeit weitere Fälle hinzu. Diese Liste dient jedoch als Roadmap. Meine Testliste für FizzBuzz würde ungefähr so aussehen.
quelle
Der "echte" Code ist der Code, den Sie schreiben, um Ihren Test zu bestehen. Wirklich . So einfach ist das.
Wenn Leute darüber reden, das Nötigste zu schreiben, um den Test grün zu machen, bedeutet das nur, dass Ihr echter Code dem YAGNI-Prinzip folgen sollte .
Die Idee des Refactor-Schritts ist nur, das aufzuräumen, was Sie geschrieben haben, sobald Sie zufrieden sind, dass es den Anforderungen entspricht.
Solange die Tests, die Sie schreiben, tatsächlich Ihre Produktanforderungen umfassen, ist der Code vollständig, sobald sie bestanden wurden. Denken Sie darüber nach, wenn alle Ihre Geschäftsanforderungen einen Test haben und alle diese Tests grün sind, was gibt es dann mehr zu schreiben? (Okay, im wirklichen Leben haben wir normalerweise keine vollständige Testabdeckung, aber die Theorie ist solide.)
quelle
switch
für jeden Komponententest ein großer Fall sein, der alle Tests besteht und für alle anderen Eingaben fehlschlägt.switch
" war als logisches Extrem von "Das Nötigste schreiben, um die Tests grün zu machen" gedacht. Ich betrachte die Frage des OP als: Wo in TDD ist das Prinzip, das dies vermeidet, großswitch
?Die kurze Antwort ist, dass der "echte Code" der Code ist, der den Test bestanden hat. Wenn Sie Ihren Test mit etwas anderem als echtem Code bestehen können, fügen Sie weitere Tests hinzu!
Ich bin damit einverstanden, dass viele Tutorials zu TDD einfach sind. Das wirkt gegen sie. Ein zu einfacher Test für eine Methode, die beispielsweise 3 + 8 berechnet, hat wirklich keine andere Wahl, als auch 3 + 8 zu berechnen und das Ergebnis zu vergleichen. Es sieht also so aus, als würden Sie nur Code duplizieren, und das Testen ist sinnlos und fehleranfällig.
Wenn Sie gut testen können, erfahren Sie, wie Sie Ihre Anwendung strukturieren und wie Sie Ihren Code schreiben. Wenn Sie Probleme haben, vernünftige und hilfreiche Tests zu finden, sollten Sie Ihr Design wahrscheinlich ein wenig überdenken. Ein gut konzipiertes System ist einfach zu testen - das heißt, sinnvolle Tests sind einfach zu überlegen und zu implementieren.
Wenn Sie Ihre Tests zuerst schreiben, beobachten Sie, wie sie fehlschlagen, und schreiben Sie dann den Code, der sie bestehen lässt. Dies ist eine Disziplin, um sicherzustellen, dass Ihr gesamter Code über entsprechende Tests verfügt. Ich folge dieser Regel nicht sklavisch, wenn ich programmiere. Oft schreibe ich Tests nach. Aber zuerst Tests zu machen hilft dir, ehrlich zu bleiben. Mit etwas Erfahrung werden Sie bemerken, wenn Sie sich in eine Ecke kodieren, auch wenn Sie nicht zuerst Tests schreiben.
quelle
assertEqual(plus(3,8), 11)
, nichtassertEqual(plus(3,8), my_test_implementation_of_addition(3,8))
. Für komplexere Fälle, schauen Sie immer für ein Mittel zum Nachweis das Ergebnis korrekt, andere als dynamisch das korrekte Ergebnis im Test Berechnung und Überprüfung Gleichheit.plus(3,8)
das richtige Ergebnis zurückgegeben wurde, indem Sie 3 davon subtrahieren, 8 davon subtrahieren und das Ergebnis gegen 0 prüfen. Dies ist so offensichtlich äquivalentassertEqual(plus(3,8), 3+8)
wie a Ein bisschen absurd, aber wenn der zu testende Code etwas komplizierteres als nur eine Ganzzahl erstellt, ist es oft der richtige Ansatz, das Ergebnis zu nehmen und jedes Teil auf Richtigkeit zu überprüfen. Alternativ etwas wiefor (i=0, j=10; i < 10; ++i, ++j) assertEqual(plus(i, 10), j)
plus()
10 zu Dingen hinzufügt. Wir verlassen uns natürlich immer noch auf die vom Programmierer verifizierten Anfangsschleifenwerte.Manchmal können einige Beispiele zu TDD irreführend sein. Wie andere bereits erwähnt haben, handelt es sich bei dem Code, den Sie schreiben, um Tests zu bestehen, um den tatsächlichen Code.
Aber denken Sie nicht, dass der wahre Code wie Magie aussieht - das ist falsch. Sie müssen besser verstehen, was Sie erreichen möchten, und dann müssen Sie den Test entsprechend auswählen, ausgehend von den einfachsten Fällen und Eckfällen.
Wenn Sie beispielsweise ein Lexer schreiben müssen, beginnen Sie mit einer leeren Zeichenfolge, dann mit einer Reihe von Leerzeichen, dann einer Zahl, dann mit einer Zahl, die von Leerzeichen umgeben ist, dann einer falschen Zahl usw. Diese kleinen Transformationen führen Sie zu der richtige Algorithmus, aber Sie springen nicht vom einfachsten Fall zu einem hochkomplexen Fall, der dumm gewählt wurde, um den echten Code zu erledigen.
Bob Martin erklärt es hier perfekt .
quelle
Der Refactor-Teil wird aufgeräumt, wenn Sie müde sind und nach Hause wollen.
Wenn Sie ein Feature hinzufügen möchten, ändern Sie den Refactor-Teil vor dem nächsten Test. Sie überarbeiten den Code, um Platz für die neue Funktion zu schaffen. Sie tun dies, wenn Sie wissen, was diese neue Funktion sein wird. Nicht, wenn du es dir nur vorstellst.
Dies kann so einfach sein wie das Umbenennen
GreetImpl
in,GreetWorld
bevor Sie eineGreetMom
Klasse erstellen (nachdem Sie einen Test hinzugefügt haben), um eine Funktion hinzuzufügen, die "Hi Mom" ausgibt.quelle
Der reale Code würde jedoch in der Refactor-Phase der TDD-Phase erscheinen. Dh der Code, der Teil der endgültigen Version sein sollte.
Tests sollten bei jeder Änderung durchgeführt werden.
Das Motto des TDD-Lebenszyklus wäre: RED GREEN REFACTOR
ROT : Schreiben Sie die Tests
GRÜN : Machen Sie einen ehrlichen Versuch, Funktionscode zu erhalten, der die Tests so schnell wie möglich besteht: doppelter Code, unklar benannte Variablen-Hacks höchster Ordnung usw.
REFAKTOR : Bereinigen Sie den Code und benennen Sie die Variablen richtig. TROCKNEN Sie den Code.
quelle
In der roten Phase schreiben Sie Code.
In der Refactoring- Phase besteht das primäre Ziel darin, Code zu löschen .
In der roten Phase unternehmen Sie alles, um den Test so schnell wie möglich und um jeden Preis zu bestehen . Sie ignorieren völlig, was Sie jemals von guten Codierungspraktiken oder Designmustern gehört haben. Es ist alles, was zählt, den Test grün zu machen.
In der Refactoring- Phase bereinigen Sie das Chaos, das Sie gerade gemacht haben. Nun sehen Sie zunächst, ob die gerade vorgenommene Änderung die am häufigsten in der Liste der Transformationsprioritäten aufgeführte Änderung ist und ob Code-Duplikate vorhanden sind, die Sie höchstwahrscheinlich durch Anwenden eines Entwurfsmusters entfernen können.
Schließlich verbessern Sie die Lesbarkeit, indem Sie Bezeichner umbenennen und magische Zahlen und / oder Zeichenfolgen in Konstanten extrahieren .
Vielen Dank für den Hinweis.
Es ist also die grüne Phase, in der Sie den realen Code schreiben
In der roten Phase schreiben Sie die ausführbare Spezifikation ...
quelle
Sie schreiben die ganze Zeit Real Code .
Bei jedem Schritt schreiben Sie Code, um die Bedingungen zu erfüllen, die Ihr Code für zukünftige Anrufer Ihres Codes erfüllt (die Sie sein können oder nicht ...).
Sie denken, Sie schreiben keinen nützlichen ( echten ) Code, weil Sie ihn in einem Moment möglicherweise überarbeiten.
Dies bedeutet, dass, obwohl Sie den Code ändern, die Bedingungen, unter denen der Code erfüllt ist, unverändert bleiben. Und die Überprüfungen ( Tests ), die Sie implementiert haben, um Ihren Code zu überprüfen, sind bereits vorhanden, um zu überprüfen, ob Ihre Änderungen etwas geändert haben. Also ist der Code, den Sie die ganze Zeit geschrieben haben, nur auf eine andere Art und Weise enthalten.
Ein weiterer Grund, warum Sie vielleicht denken, dass es sich nicht um echten Code handelt, ist, dass Sie Beispiele erstellen, in denen das Endprogramm bereits von Ihnen vorhergesehen werden kann. Dies ist sehr gut, da es zeigt, dass Sie Kenntnisse über die Domäne haben, in der Sie programmieren.
Oft befinden sich Programmierer jedoch in einer Domäne, die neu und für sie unbekannt ist . Sie wissen nicht, wie das Endergebnis aussehen wird, und TDD ist eine Technik, mit der Sie Schritt für Schritt Programme schreiben, unser Wissen über die Funktionsweise dieses Systems dokumentieren und überprüfen können, ob unser Code auf diese Weise funktioniert.
Als ich The Book (*) auf TDD las, war für mich das wichtigste Merkmal die: TODO-Liste. Es hat mir gezeigt, dass TDD auch eine Technik ist, die Entwicklern hilft, sich auf eine Sache zu konzentrieren. Dies ist also auch eine Antwort auf Ihre Frage: Wie viel Real-Code muss geschrieben werden ? Ich würde genug Code sagen, um mich jeweils auf eine Sache zu konzentrieren.
(*) "Test Driven Development: By Example" von Kent Beck
quelle
Sie schreiben keinen Code, um die Tests zum Scheitern zu bringen.
Sie schreiben Ihre Tests, um zu definieren, wie der Erfolg aussehen soll. Alle Tests sollten zunächst fehlschlagen, da Sie den Code, der erfolgreich sein wird, noch nicht geschrieben haben.
Der springende Punkt beim Schreiben anfangs fehlgeschlagener Tests ist, zwei Dinge zu tun:
Der Punkt hinter Rot-Grün-Refaktor ist, dass Sie durch das Schreiben der richtigen Tests zunächst die Gewissheit gewinnen, dass der Code, den Sie zum Bestehen der Tests geschrieben haben, korrekt ist, und dass Sie mit der Gewissheit refaktorieren können, dass Ihre Tests Sie so bald wie möglich informieren Etwas bricht zusammen, sodass Sie es sofort reparieren können.
Nach meiner eigenen Erfahrung (C # /. NET) ist reines Test-first ein unerreichbares Ideal, da Sie einen Aufruf einer Methode, die noch nicht existiert, nicht kompilieren können. Bei "test first" geht es also wirklich darum, Schnittstellen und Stub-Implementierungen zuerst zu codieren und dann Tests gegen die Stubs zu schreiben (die anfangs fehlschlagen), bis die Stubs richtig ausgearbeitet sind. Ich schreibe nie "fehlerhaften Code", sondern baue nur aus Stubs.
quelle
Ich denke, Sie können zwischen Unit-Tests und Integrationstests verwechselt werden. Ich glaube, es kann auch Abnahmetests geben, aber das hängt von Ihrem Prozess ab.
Sobald Sie alle kleinen "Einheiten" getestet haben, testen Sie sie alle zusammengebaut oder "integriert". Das ist normalerweise ein ganzes Programm oder eine ganze Bibliothek.
In dem Code, den ich für die Integration geschrieben habe, wird eine Bibliothek mit verschiedenen Testprogrammen getestet, die Daten lesen und an die Bibliothek weiterleiten. Anschließend werden die Ergebnisse überprüft. Dann mache ich es mit Fäden. Dann mache ich es mit Fäden und Gabel () in der Mitte. Dann starte ich es und töte -9 nach 2 Sekunden, dann starte ich es und überprüfe seinen Wiederherstellungsmodus. Ich fussel es durch. Ich quäle es auf alle möglichen Arten.
Das alles testet AUCH, aber ich habe keine hübsche rot / grüne Anzeige für die Ergebnisse. Entweder gelingt es, oder ich stöbere in ein paar tausend Zeilen Fehlercode, um herauszufinden, warum.
Hier testen Sie den "echten Code".
Und ich habe gerade darüber nachgedacht, aber vielleicht wissen Sie nicht, wann Sie mit dem Schreiben von Komponententests fertig sein sollen. Sie sind mit dem Schreiben von Komponententests fertig, wenn Ihre Tests alles ausführen, was Sie angegeben haben. Manchmal kann es vorkommen, dass Sie bei allen Fehlerbehandlungs- und Randfällen den Überblick verlieren. Daher möchten Sie möglicherweise eine nette Testgruppe von Happy-Path-Tests erstellen, die einfach die Spezifikationen durchgehen.
quelle
Auf den Titel der Frage: "Wann schreiben Sie den" echten "Code in TDD?" Lautet die Antwort: "kaum jemals" oder "sehr langsam".
Sie klingen wie ein Student, also werde ich antworten, als würde ich einen Studenten beraten.
Sie werden viele Codierungstheorien und -techniken lernen. Sie eignen sich hervorragend, um sich die Zeit bei überteuerten Kursen zu vertreiben, haben jedoch nur einen geringen Nutzen für Sie, den Sie in der Hälfte der Zeit nicht in einem Buch lesen konnten.
Die Aufgabe eines Programmierers besteht ausschließlich darin, Code zu erstellen. Code, der wirklich gut funktioniert. Aus diesem Grund plant der Codierer den Code in Ihrem Kopf, auf Papier, in einer geeigneten Anwendung usw., und Sie planen, mögliche Fehler / Löcher im Voraus zu umgehen, indem Sie vor dem Codieren logisch und seitlich nachdenken.
Sie müssen jedoch wissen, wie Sie Ihre Anwendung auflösen können, um anständigen Code entwerfen zu können. Wenn Sie beispielsweise nicht über Little Bobby Table (xkcd 327) informiert wären, würden Sie Ihre Eingaben wahrscheinlich nicht bereinigen, bevor Sie mit der Datenbank arbeiten, sodass Sie Ihre Daten nicht um dieses Konzept herum sichern können.
TDD ist nur ein Workflow, der entwickelt wurde, um die Fehler in Ihrem Code zu minimieren, indem die Tests erstellt werden, bevor Sie Ihre Anwendung codieren, da das Codieren umso schwieriger werden kann, je mehr Code Sie einführen und Sie Fehler vergessen, an die Sie früher gedacht haben. Sobald Sie glauben, dass Sie Ihre Anwendung fertiggestellt haben, führen Sie die Tests aus und boomen. Hoffentlich werden bei Ihren Tests Fehler festgestellt.
TDD ist nicht - wie manche Leute glauben -, einen Test zu schreiben, mit minimalem Code zu bestehen, einen weiteren Test zu schreiben, mit minimalem Code zu bestehen usw. Stattdessen hilft es Ihnen, sicher zu programmieren. Dieses Ideal des kontinuierlichen Umgestaltens von Code, damit er mit Tests funktioniert, ist idiotisch, aber es ist ein schönes Konzept für Schüler, da sie sich dadurch gut fühlen, wenn sie eine neue Funktion hinzufügen und noch lernen, wie man Code ...
Bitte tappen Sie nicht in diese Falle und sehen Sie, welche Rolle Sie beim Codieren spielen - der Job eines Codierers besteht ausschließlich darin, Code zu produzieren. Code, der wirklich gut funktioniert. Denken Sie jetzt daran, dass Sie als professioneller Programmierer auf dem Laufenden sind und es Ihrem Kunden egal ist, ob Sie 100.000 Behauptungen oder 0 geschrieben haben. Sie möchten nur Code, der funktioniert. Wirklich gut.
quelle