Wann schreiben Sie den "echten" Code in TDD?

147

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.

Johnny
quelle
193
Nach dem TDD gehen die Leute für die Nacht nach Hause.
Hobbs
14
Warum denkst du, ist der Code, den du geschrieben hast, nicht echt?
Goyo
2
@RubberDuck Mehr als die anderen Antworten. Ich bin sicher, ich werde bald darauf verweisen. Es ist immer noch irgendwie fremd, aber ich werde es nicht aufgeben. Was Sie sagten, ergab einen Sinn. Ich versuche nur, es in meinem Kontext oder einer normalen Geschäftsanwendung sinnvoll zu machen. Vielleicht ein Inventarsystem oder ähnliches. Ich muss darüber nachdenken. Ich bin aber dankbar für deine Zeit. Vielen Dank.
Johnny
1
Die Antworten treffen bereits den Nagel auf den Kopf, aber solange alle Ihre Tests bestanden sind und Sie keine neuen Tests / Funktionen benötigen, kann davon ausgegangen werden, dass der Code, den Sie haben, fertig ist und nicht mehr funktioniert.
ESR
3
Es gibt eine Annahme in der Frage, die problematisch sein kann in "Ich habe ein ziemlich komplexes Objekt mit einer komplexen Methode". In TDD schreiben Sie Ihre Tests zuerst, sodass Sie mit einem relativ einfachen Code beginnen. Dadurch müssen Sie eine testfreundliche Struktur codieren, die modular aufgebaut sein muss. So wird ein komplexes Verhalten erzeugt, indem einfachere Objekte kombiniert werden. Wenn Sie mit einem ziemlich komplexen Objekt oder einer ziemlich komplexen Methode enden, ist dies der Zeitpunkt, an dem Sie das Refactor durchführen
Borjab

Antworten:

243

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.

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.

Assert.Equal("1", FizzBuzz(1));

Einfach peazy.

public String FizzBuzz(int n) {
    return 1.ToString();
}

Nicht das, was Sie als echten Code bezeichnen würden, oder? Fügen wir einen Test hinzu, der eine Änderung erzwingt.

Assert.Equal("2", FizzBuzz(2));

Wir könnten so etwas albernes machen if n == 1, aber wir werden zur vernünftigen Lösung übergehen.

public String FizzBuzz(int n) {
    return n.ToString();
}

Cool. Dies funktioniert für alle Nicht-FizzBuzz-Nummern. Was ist die nächste Eingabe, die eine Änderung des Produktionscodes erzwingt?

Assert.Equal("Fizz", FizzBuzz(3));

public String FizzBuzz(int n) {
    if (n == 3)
        return "Fizz";
    return n.ToString();
}

Und wieder. Schreiben Sie einen Test, der noch nicht bestanden wird.

Assert.Equal("Fizz", FizzBuzz(6));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    return n.ToString();
}

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.

Assert.Equal("Buzz", FizzBuzz(5));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n == 5)
        return "Buzz"
    return n.ToString();
}

Und wieder wissen wir, dass es einen anderen Fall gibt, den wir behandeln müssen.

Assert.Equal("Buzz", FizzBuzz(10));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n % 5 == 0)
        return "Buzz"
    return n.ToString();
}

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.

private bool isDivisibleBy(int divisor, int input) {
    return (input % divisor == 0);
}

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

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.

Assert.Equal("FizzBuzz", FizzBuzz(15));

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n) && isDivisibleBy(5, n))
        return "FizzBuzz";
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

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.

public String FizzBuzz(int n) {

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

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.

public String FizzBuzz(int n) {

    if (n < 1)
        throw new InvalidArgException("n must be >= 1);

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

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.

  • Negativ
  • Null
  • Ein
  • Zwei
  • Drei
  • Vier
  • Fünf
  • Sechs (nicht triviales Vielfaches von 3)
  • Neun (3 im Quadrat)
  • Zehn (nicht triviales Vielfaches von 5)
  • 15 (Vielfaches von 3 & 5)
  • 30 (nicht triviales Vielfaches von 3 & 5)
Badeente
quelle
3
Kommentare sind nicht für eine längere Diskussion gedacht. Diese Unterhaltung wurde in den Chat verschoben .
maple_shaft
47
Es sei denn, ich verstehe diese Antwort völlig falsch: "Wir könnten etwas Dummes tun, wenn n == 1, aber wir springen zur vernünftigen Lösung." - das Ganze war albern. Wenn Sie wissen, dass Sie eine Funktion benötigen, die <spec> ausführt, schreiben Sie Tests für <spec> und überspringen Sie den Teil, in dem Sie Versionen schreiben, bei denen offensichtlich <spec> fehlschlägt. Wenn Sie in <spec> einen Fehler finden, sollten Sie zunächst einen Test schreiben, um sicherzustellen, dass Sie ihn vor dem Fix ausführen und die Testdurchläufe nach dem Fix beobachten können. Aber all diese Zwischenschritte müssen nicht vorgetäuscht werden.
GManNickG
16
Die Kommentare, die auf die Hauptmängel in dieser Antwort und in TDD im Allgemeinen hinweisen, wurden in den Chat verschoben. Wenn Sie TDD in Betracht ziehen, lesen Sie bitte den "Chat". Leider sind die "Qualitäts" -Kommentare jetzt in einer Menge Chats versteckt, die zukünftige Studenten lesen können.
user3791372
2
@GManNickG Ich glaube, es geht darum, die richtige Menge an Tests zu erhalten. Das Schreiben der Tests im Voraus macht es leicht, zu übersehen, welche Sonderfälle getestet werden sollten, was entweder dazu führt, dass Situationen in den Tests nicht angemessen abgedeckt werden, oder dass im Wesentlichen dieselbe Situation in den Tests unnötig oft abgedeckt wird. Wenn Sie das ohne diese Zwischenschritte schaffen, großartig! Das kann aber noch nicht jeder, das braucht Übung.
HDV
1
Und hier ein Zitat von Kent Beck zum Refactoring: "Jetzt, da der Test läuft, können wir (wie in" real ") die Implementierung von summary () realisieren." Anschließend wandelt er eine Konstante in eine Variable um. Ich fand, dass dieses Zitat gut zur Frage passte.
Chris Wohlert
46

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.)

GenericJon
quelle
45
Unit-Tests können Ihre Produktanforderungen nicht einmal für relativ unbedeutende Anforderungen abdecken. Bestenfalls wird der Eingabe-Ausgabe-Raum abgetastet, und die Idee ist, dass Sie (korrekt) den gesamten Eingabe-Ausgabe-Raum verallgemeinern. Natürlich könnte Ihr Code switchfür jeden Komponententest ein großer Fall sein, der alle Tests besteht und für alle anderen Eingaben fehlschlägt.
Derek Elkins
8
@DerekElkins TDD schreibt nicht bestandene Tests vor. Unit-Tests nicht fehlgeschlagen.
Taemyr
6
@DerekElkins deshalb schreibst du nicht nur Unit-Tests und es gibt auch die allgemeine Annahme, dass du versuchst, etwas zu machen, das nicht nur gefälscht ist!
Jonrsharpe
36
@jonrsharpe Mit dieser Logik würde ich niemals triviale Implementierungen schreiben. ZB im FizzBuzz-Beispiel in der RubberDuck-Antwort (die nur Komponententests verwendet) ist die erste Implementierung eindeutig "nur eine Fälschung". Mein Verständnis der Frage ist genau diese Zweiteilung zwischen dem Schreiben von Code, von dem Sie wissen, dass er unvollständig ist, und dem Code, von dem Sie wirklich glauben, dass er die Anforderung, den "echten Code", implementiert. Mein "Big 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?
Derek Elkins
2
@GenericJon Das ist meiner Erfahrung nach ein bisschen zu optimistisch :) Zum einen gibt es Leute, die Spaß an sinnloser, sich wiederholender Arbeit haben. Sie werden mit einer riesigen Schaltaussage zufriedener sein als mit einer "komplizierten Entscheidungsfindung". Und um ihren Job zu verlieren, brauchen sie entweder jemanden, der sie mit der Technik vertraut macht (und sie haben besser gute Beweise dafür, dass sie tatsächlich die Chancen / das Geld des Unternehmens verlieren!) Oder sie tun es außergewöhnlich schlecht. Nachdem ich die Wartung vieler solcher Projekte übernommen habe, kann ich feststellen, dass es für sehr naiven Code einfach ist, Jahrzehnte zu halten, solange der Kunde zufrieden ist (und zahlt).
Luaan
14

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.

Carl Raymond
quelle
6
Persönlich wäre der Test, den ich schreiben würde assertEqual(plus(3,8), 11), nicht assertEqual(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.
Steve Jessop
Für eine wirklich alberne Vorgehensweise in diesem Beispiel könnten Sie beweisen, dass 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 äquivalent assertEqual(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)
Steve Jessop
... da dies die große Angst vermeidet, die darin besteht, dass wir beim Schreiben des Tests den gleichen Fehler machen, den wir beim Thema "Wie man 10 addiert" im Live-Code gemacht haben. Der Test vermeidet daher sorgfältig, Code zu schreiben, der 10 zu etwas hinzufügt, während der Test plus()10 zu Dingen hinzufügt. Wir verlassen uns natürlich immer noch auf die vom Programmierer verifizierten Anfangsschleifenwerte.
Steve Jessop
4
Ich möchte nur darauf hinweisen, dass es auch dann eine gute Idee ist, wenn Sie Tests nachträglich schreiben. Suchen Sie einen Teil des Codes, der für Ihre Arbeit von entscheidender Bedeutung ist, ändern Sie ihn ein wenig (z. B. ersetzen Sie ein + durch ein - oder was auch immer), führen Sie die Tests aus und beobachten Sie, wie sie fehlschlagen, machen Sie die Änderung rückgängig und sehen Sie zu, wie sie bestanden werden. Oft habe ich das getan, aber der Test schlägt nicht fehl, was ihn schlimmer als nutzlos macht: Er testet nicht nur nichts, er gibt mir auch das falsche Vertrauen, dass etwas getestet wird!
Warbo
6

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 .

Victor Cejudo
quelle
5

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 GreetImplin, GreetWorldbevor Sie eine GreetMomKlasse erstellen (nachdem Sie einen Test hinzugefügt haben), um eine Funktion hinzuzufügen, die "Hi Mom" ​​ausgibt.

kandierte_orange
quelle
1

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.

graeme
quelle
6
Ich weiß, was Sie über die "grüne" Phase sagen, aber dies impliziert, dass es angemessen sein könnte, die Rückgabewerte für die harte Verdrahtung festzulegen, damit die Tests bestanden werden. Meiner Erfahrung nach sollte "Green" ein ehrlicher Versuch sein, Arbeitscode so zu gestalten, dass er die Anforderungen erfüllt. Er ist möglicherweise nicht perfekt, sollte aber so vollständig und "versandfähig" sein, wie der Entwickler es im ersten Durchgang schaffen kann. Refactoring wird wahrscheinlich einige Zeit später durchgeführt, nachdem Sie weitere Entwicklungen durchgeführt haben und die Probleme mit dem ersten Durchgang offensichtlicher werden und sich Möglichkeiten für DRY ergeben.
Mcottle
2
@mcottle: Sie werden überrascht sein, wie viele Implementierungen eines Nur-Holen-Repositorys Werte in der Codebasis fest codieren können. :)
Bryan Boettcher
6
Warum sollte ich jemals Mistcode schreiben und ihn bereinigen, wenn ich guten Code in Produktionsqualität fast so schnell ausgeben kann, wie ich tippen kann? :)
Kaz
1
@Kaz Da auf diese Weise das Risiko besteht, dass Sie nicht getestetes Verhalten hinzufügen . Die einzige Möglichkeit, um sicherzustellen, dass für jedes gewünschte Verhalten ein Test durchgeführt wird, besteht darin, die möglichen Änderungen vorzunehmen, unabhängig davon, wie beschissen sie sind. Manchmal bringt das folgende Refactoring einen neuen Ansatz hervor, an den Sie nicht im Voraus gedacht haben ...
Timothy Truckle,
1
@TimothyTruckle Was ist, wenn es 50 Minuten dauert, um die einfachste mögliche Änderung zu finden, aber nur 5, um die zweitleichteste mögliche Änderung zu finden? Fahren wir mit dem zweiteinfachsten fort oder suchen wir weiter nach dem einfachsten?
Kaz
1

Wann schreiben Sie den "echten" Code in TDD?

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 .


Es ist kein Rot-Refaktor, es ist ein Rot-Grün-Refaktor. - Rob Kinyon

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 ...

Timothy Truckle
quelle
Es ist kein Rot-Refaktor, es ist ein Rot-Grün-Refaktor. Das "Rot" ist, dass Sie Ihre Testsuite von Grün (alle Tests bestehen) auf Rot (ein Test schlägt fehl) setzen. Das "Grün" ist, wo Sie Ihre Testsuite von Rot (ein Test schlägt fehl) zu Grün (alle Tests bestehen) schlampig nehmen. Im "Refactor" nehmen Sie Ihren Code auf und machen ihn hübsch, während Sie alle Tests bestehen.
Rob Kinyon
1

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.

Beim Code-Refactoring wird vorhandener Computercode umstrukturiert - das Factoring wird geändert, ohne dass das externe Verhalten geändert wird.

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

Robert Andrzejuk
quelle
2
"Test Driven Development: By Example" von Kent Beck
Robert Andrzejuk
1

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:

  1. Deckt alle Fälle ab - alle nominellen Fälle, alle Randfälle usw.
  2. Validieren Sie Ihre Tests. Wie können Sie sicher sein, dass sie einen Fehler zuverlässig melden, wenn einer auftritt, wenn Sie sie jemals bestehen sehen?

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.

Zenilogix
quelle
0

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.

Zan Lynx
quelle
(Es ist = besitzergreifend, es ist = "es ist" oder "es hat". Sehen Sie zum Beispiel, wie man es und es verwendet .)
Peter Mortensen
-6

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.

user3791372
quelle
3
Ich bin noch nicht einmal in der Nähe eines Studenten, aber ich lese und versuche, gute Techniken anzuwenden und professionell zu sein. In diesem Sinne bin ich ein "Student". Ich stelle nur sehr grundlegende Fragen, weil ich so bin. Ich möchte genau wissen, warum ich das tue, was ich tue. Der Kern der Sache. Wenn ich das nicht verstehe, mag ich es nicht und beginne Fragen zu stellen. Ich muss wissen warum, wenn ich es benutzen werde. TDD scheint in gewisser Hinsicht intuitiv gut zu sein, wie zu wissen, was Sie zum Erstellen und Durchdenken von Dingen benötigen, aber die Implementierung war schwer zu verstehen. Ich glaube, ich habe jetzt einen besseren Überblick.
Johnny
4
Das sind die Regeln von TDD. Sie können Code schreiben, wie Sie möchten, aber wenn Sie diese drei Regeln nicht befolgen, tun Sie nichts gegen TDD.
Sean Burton
2
"Regeln" einer Person? TDD ist ein Vorschlag, der Ihnen beim Codieren hilft, keine Religion. Es ist traurig zu sehen, dass so viele Menschen sich so anal an eine Idee halten. Auch die Entstehung von TDD ist umstritten.
user3791372
2
@ user3791372 TDD ist ein sehr strenger und klar definierter Prozess. Auch wenn viele denken, dass dies nur bedeutet "Machen Sie ein paar Tests, wenn Sie programmieren", ist dies nicht der Fall. Lassen Sie uns versuchen, Begriffe hier nicht zu verwechseln. Diese Frage bezieht sich auf den TDD-Prozess und nicht auf allgemeine Tests.
Alex