TDD und Versionskontrolle

25

Ich lerne gerade etwas über TDD und versuche es in meinen persönlichen Projekten in die Praxis umzusetzen. Bei vielen dieser Projekte habe ich auch die Versionskontrolle intensiv genutzt. Ich interessiere mich für das Zusammenspiel dieser beiden Tools in einem typischen Arbeitsablauf, insbesondere wenn es um die Maxime geht, Commits klein zu halten. Hier sind einige Beispiele, die mir einfallen:

  1. Ich starte ein neues Projekt und schreibe einen einfachen Test, um eine noch nicht vorhandene Klasse zu erstellen. Sollte ich den Test durchführen, bevor ich die Klasse schreibe, obwohl der Test nicht kompiliert wird? Oder sollte ich die Mindestmenge an Code herausfiltern, die erforderlich ist, damit der Test vor dem Festschreiben kompiliert wird?

  2. Ich finde einen Fehler und schreibe einen Test, um ihn neu zu erstellen. Soll ich den fehlgeschlagenen Test festschreiben oder die Fehlerbehebung implementieren und dann festschreiben?

Dies sind die beiden Beispiele, die sofort in den Sinn kommen. Fühlen Sie sich frei, zusätzliche Beispiele in Ihrer Antwort anzugeben.

Bearbeiten:

In beiden Beispielen bin ich davon ausgegangen, dass ich unmittelbar nach dem Schreiben des Tests Code schreiben werde, um den Test zu bestehen. Es kann auch eine andere Situation eintreten: Ich arbeite mehrere Stunden mit TDD an einem Projekt, ohne dies zu bestätigen. Wenn ich mich endgültig festgelegt habe, möchte ich meine Arbeit in kleine Stücke aufteilen. (Git macht dies relativ einfach, selbst wenn Sie nur einige der Änderungen in einer einzelnen Datei festschreiben möchten.)

Das bedeutet, dass es bei meiner Frage genauso darum geht, was zu begehen ist, wie darum, wann zu begehen ist.

Code-Guru
quelle

Antworten:

21

Sollte ich den Test durchführen, bevor ich die Klasse schreibe, obwohl der Test nicht kompiliert wird? Oder sollte ich die Mindestmenge an Code herausfiltern, die erforderlich ist, damit der Test vor dem Festschreiben kompiliert werden kann?

Natürlich nicht. Sie sollten sowohl den Test als auch die Klasse beenden. Das Festschreiben von etwas 1 , das nicht einmal kompiliert werden kann, macht keinen Sinn und die Leute, die am selben Projekt arbeiten, werden sicherlich wütend, wenn Sie es regelmäßig tun.

Ich finde einen Fehler und schreibe einen Test, um ihn neu zu erstellen. Soll ich den fehlgeschlagenen Test festschreiben oder die Fehlerbehebung implementieren und dann festschreiben?

Nein, führen Sie keinen fehlgeschlagenen Test durch. LeBlancs Gesetz besagt:

Später ist nie gleich.

und Ihr Test kann für eine lange Zeit fehlschlagen. Es ist besser, das Problem zu beheben, sobald es erkannt wird.

Der TDD-Entwicklungsstil sagt außerdem Folgendes aus :

Die testgetriebene Entwicklung wiederholt ständig die Schritte des Hinzufügens fehlgeschlagener Testfälle, des Übergebens und des Refactorings.

Wenn Sie einen fehlgeschlagenen Test einchecken, bedeutet dies, dass Sie den Zyklus nicht abgeschlossen haben.


1 Als ich Commit sagte, meinte ich wirklich Commit für den Trunk (für Git-Benutzer sollten Sie Ihre Änderungen pushen, damit andere Entwickler sie erhalten).

BЈовић
quelle
4
"und wird sicherlich wütende Menschen dazu bringen, am selben Projekt zu arbeiten, wenn" - jemand in der SVN-Welt lebt und GIT nutzt und Sie niemanden wütend machen
Mateusz
3
Ich denke, das Festschreiben ist in Ordnung, nachdem Sie einen Test geschrieben haben. Schieben Sie ihn einfach nicht weiter, bis Sie fertig sind.
Matsemann
4
@radarbob Gilt das auch für ein DVCS, bei dem zwischen Commit und Push unterschieden wird? Ich kann mir eine Situation vorstellen, in der ich mehrere Commits für mein lokales Git-Repo vornehme, bei denen der Build beim endgültigen Commit nicht unterbrochen wird, sondern bei einem der vorläufigen Commits.
Code-Guru
6
Nein, Test nicht bestanden. Aber einer der Punkte von TDD ist genau, vor dem Codieren einen Fehlertest durchzuführen. Ein nicht bestandener Test ist also sinnvoll.
Mouviciel
4
@ Code-Guru: Für ein DVCS sollten diese Regeln lauten: "Übertragen Sie gebrochenen Code nicht an einen Zweig, von dem andere regelmäßig abrufen". Wenn andere nicht aus Ihrem lokalen Repo ziehen, kann das in jedem Zustand sein, mit dem Sie leben können.
Bart van Ingen Schenau
6

Sollte ich den Test durchführen, bevor ich die Klasse schreibe, obwohl der Test nicht kompiliert wird?

Nein.

Soll ich den nicht bestandenen Test durchführen?

Nein.

Sie sprechen hier über zwei Paradigmen:

  1. Testgetriebene Entwicklung - die nichts über das Festschreiben von Code aussagt. In der Tat erfahren Sie, wie Sie Code schreiben und wann Sie fertig sind. Also würde ich jedes "Erledigt" als Kandidaten für ein Commit betrachten.
  2. Agile Entwicklung, insbesondere: "Früh und häufig ein Commit durchführen" (für die kein TDD erforderlich ist). Dahinter steckt die Idee, sich frühzeitig in andere Komponenten des Systems zu integrieren und so frühzeitig Feedback zu erhalten. Wenn Sie ein lokales DVCS-Commit durchführen und kein Push durchführen, ist dies in dieser Hinsicht wertlos. Lokale Commits helfen nur Entwicklern, ihre Arbeit zu strukturieren.

Meine Empfehlung lautet: Folgen Sie dem Kreis von TDD, bis Ihr Code kompiliert ist, Ihre Tests grün sind und Sie etwas zum System beitragen können. Daher sollten Sie Ihre Features vertikal schneiden, zB für eine neue UI-Maske nicht das gesamte Formular erstellen und ohne die Geschäftslogik festschreiben, sondern einen winzigen Aspekt implementieren, sondern sowohl im Frontend als auch in der Geschäftslogik sowie in der Persistenzschicht .

Bei einem großen Bugfix muss nach jeder Verbesserung (z. B. Refactoring) ein Commit durchgeführt werden, auch wenn der Bug noch nicht behoben ist. Tests sollten grün sein und Code muss kompiliert werden.

Andy
quelle
5

Sicherlich fängst du mit einer gesunden Quellcodeverwaltung wie git an.

Dann können Sie nach Belieben arbeiten und an jeder Ecke etwas unternehmen - jeder Schritt oder Unterschritt ist ein faires Spiel.

Dann, bevor Sie das Zeug pushen, quetschen Sie die ganze Arbeit in einem einzigen Commit. Oder ein Paar an Stellen, an denen alles grün ist und die Komposition Sinn ergibt. Und diese vernünftigen Verpflichtungen forcieren. Erstellen Sie für den Mehrfachfall einen Zweig, den Sie mit --no-ff zusammenführen.

Die Quellcodeverwaltung ist kein Work-Tracking-System oder ein Historiker. Die Commits müssen ein sinnvolles kohärentes Delta darstellen, während der Checkout-Status mindestens kompiliert werden muss. Zwischenprodukte können für Überprüfungszwecke für eine Weile aufbewahrt werden. Wenn jedoch alles als in Ordnung angesehen wird, ist ein einziges Festschreiben pro Feature angemessen.

Balog Pal
quelle
5

Es ist mein Verständnis der Welt, dass man sich verpflichtet, einen Punkt zu markieren, zu dem es wünschenswert sein kann, zurückzukehren. Der Punkt, an dem ein Test fehlschlägt (aber kompiliert), ist definitiv ein solcher Punkt. Wenn ich in die falsche Richtung davonlaufen und versuchen würde, einen Testdurchlauf zu machen, würde ich in der Lage sein, den Code auf den Ausgangspunkt zurückzusetzen und es erneut zu versuchen. Ich kann das nicht tun, wenn ich mich nicht verpflichtet habe.

Scroog1
quelle
Ich stimme mit Ihnen ein. Ich bevorzuge es, einen anderen Zweig zu verwenden, um der Regel "Bau nicht abbrechen" zu folgen und die Änderungen im Stamm nur dann zusammenzuführen, wenn der Test bestanden wurde.
Fil
5

Sollte ich den Test durchführen, bevor ich die Klasse schreibe, obwohl der Test nicht kompiliert wird?

Bei einem Verzweigungs-SCM (ich habe gesehen, dass Sie Git verwenden) sollten Sie festlegen, wann immer Sie einen Sicherungspunkt haben möchten ("Ich habe etwas vermasselt; ich werde das Arbeitsverzeichnis auf den letzten Sicherungspunkt zurücksetzen") oder wenn Sie eine stabile Version haben. Wenn Sie über eine stabile Version verfügen (alle Tests sind erfolgreich), sollten Sie auch in Betracht ziehen, den aktuellen Feature-Zweig in Ihrem Hauptentwicklungszweig zusammenzuführen.

Oder sollte ich die Mindestmenge an Code herausfiltern, die erforderlich ist, damit der Test vor dem Festschreiben kompiliert werden kann?

Bis zu Ihnen (git gibt Ihnen die Flexibilität, sich zu engagieren, wann immer Sie möchten, ohne andere Mitglieder Ihres Teams zu beeinträchtigen, oder Ihre Fähigkeit, an verschiedenen Funktionen zu arbeiten). Stellen Sie nur sicher, dass Sie nicht mehrere unvollständige (nicht funktionierende) Features gleichzeitig in derselben Verzweigung haben (dann blockieren sie sich gegenseitig).

Ich finde einen Fehler und schreibe einen Test, um ihn neu zu erstellen. Soll ich den fehlgeschlagenen Test festschreiben oder die Fehlerbehebung implementieren und dann festschreiben?

Ich mache normalerweise zwei Commits dafür, es sei denn, der Testcode ist wirklich klein / trivial zu schreiben.

Dies sind die beiden Beispiele, die sofort in den Sinn kommen. Fühlen Sie sich frei, zusätzliche Beispiele in Ihrer Antwort anzugeben.

Bearbeiten:

In beiden Beispielen ging ich davon aus, dass ich unmittelbar nach dem Schreiben des Tests Code schreiben werde, um den Test zu bestehen.

Das könnte eine falsche Annahme sein. Wenn Sie alleine arbeiten (persönliches Projekt), hindert Sie nichts daran, dies immer zu tun. In einem meiner erfolgreichsten Projekte (im Hinblick auf die Aufrechterhaltung einer hohen Codequalität und TDD während der gesamten Entwicklung des Projekts) haben wir Tests manchmal Wochen vor der Implementierung definiert (dh wir würden sagen, dass der Test "test_FOO_with_null_first_parameter" jetzt als leere Funktion definiert ist und Dann machten wir einen Sprint (oder einen halben Sprint) manchmal einen Monat später, um nur die Testabdeckung für das Modul zu erhöhen. Da wir die Tests bereits deklariert hatten, war es einfach abzuschätzen.

Es kann auch eine andere Situation eintreten: Ich arbeite mehrere Stunden mit TDD an einem Projekt, ohne dies zu bestätigen. Wenn ich mich endgültig festgelegt habe, möchte ich meine Arbeit in kleine Stücke aufteilen. (Git macht dies relativ einfach, auch wenn Sie nur einige der Änderungen in einer einzelnen Datei festschreiben möchten.) Dies bedeutet, dass meine Frage sich genauso darauf bezieht, was festgeschrieben werden soll, wie darauf, wann festgeschrieben werden soll.

Ich würde sagen, auf jeden Fall verpflichten, Backup-Punkte zu erstellen . Dies funktioniert sehr gut für Erprobungstests ("Ich füge nur einige Ausdrucke in der gesamten Codebasis hinzu, führe sie aus und git reset --hardentferne sie, wenn ich fertig bin) und für das Prototyping.

utnapistim
quelle
2
Seien Sie vorsichtig mit der Empfehlung, git reset --hard. Es ist einer der wenigen Befehle in git, die dazu führen, dass Sie Arbeit verlieren.
Gnash117
2

Wenn immer möglich, arbeite ich in meinem Arbeitsablauf unsicher in einer persönlichen Versionskontrollabteilung. So kann ich versuchen, fehlschlagen, es bei Bedarf erneut versuchen, bis es funktioniert, und mich nur dann auf das größere Projekt festlegen, wenn ich über tatsächlichen Arbeitscode verfüge.

Aus TDD-Sicht stellt sich die Frage: "Checken Sie den Test zuerst ein?" hängt vollständig von dem Code ab, an dem Sie arbeiten. Wenn es sich um einen neuen Code handelt, checken Sie nichts ein, bis Sie etwas gefunden haben, das es wert ist, eingecheckt zu werden. Wenn es sich jedoch um einen Fehler handelt, der in bereits kompiliertem oder ausgeliefertem Code gefunden wurde, lohnt es sich, einen Test einzuchecken, um den Fehler VON SICH AUS zu reproduzieren der Code.

(Wenn Ihr Shop über einen automatisierten Build-Prozess verfügt, der beim Fehlschlagen von Komponententests abstürzt, möchten Sie möglicherweise erst dann einen fehlgeschlagenen Test einchecken, wenn Sie den Fehler behoben haben. Dies scheint jedoch eine seltsame Vorgehensweise zu sein, da "find" und Fehler dokumentieren "und" Fehler beheben "können von zwei völlig unterschiedlichen Teams durchgeführt werden.)

DougM
quelle