Ich habe eine Weile versucht zu lernen, Komponententests für meinen Code zu schreiben.
Am Anfang habe ich mit echtem TDD angefangen, bei dem ich keinen Code geschrieben habe, bis ich zuerst einen fehlerhaften Test geschrieben hatte.
Vor kurzem musste ich jedoch ein heikles Problem lösen, das viel Code beinhaltete. Nachdem ich ein paar Wochen damit verbracht hatte, Tests und dann Code zu schreiben, kam ich zu dem unglücklichen Schluss, dass mein gesamter Ansatz nicht funktionieren würde, und ich musste zwei Wochen Arbeit streichen und von vorne anfangen.
Dies ist eine schlimme Entscheidung, wenn Sie gerade den Code geschrieben haben, aber wenn Sie auch mehrere hundert Unit-Tests geschrieben haben, wird es noch emotionaler, alles einfach wegzuwerfen.
Ich kann nicht anders, als zu denken, dass ich drei oder vier Tage Zeit mit dem Schreiben dieser Tests verschwendet habe, wenn ich den Code nur als Proof of Concept hätte zusammenstellen und die Tests anschließend schreiben können, sobald ich mit meinem Ansatz zufrieden war.
Wie gehen Menschen, die TDD richtig praktizieren, mit solchen Situationen um? Gibt es in einigen Fällen Gründe, die Regeln zu biegen, oder schreiben Sie die Tests immer zuerst sklavisch, auch wenn sich dieser Code als unbrauchbar herausstellt?
quelle
Antworten:
Ich habe das Gefühl, dass es hier zwei Probleme gibt. Das erste ist, dass Sie nicht im Voraus erkannt haben, dass Ihr ursprünglicher Entwurf möglicherweise nicht der beste Ansatz ist. Wenn Sie dies im Voraus gewusst haben, haben Sie sich möglicherweise dazu entschlossen, einen oder zwei schnelle Wegwerf-Prototypen zu entwickeln, die möglichen Designoptionen zu erkunden und zu bewerten, welcher Weg der vielversprechendste ist. Beim Prototyping müssen Sie keinen Code für die Produktionsqualität schreiben und nicht jede Ecke (oder überhaupt) einem Komponententest unterziehen, da Sie sich ausschließlich auf das Lernen und nicht auf das Polieren des Codes konzentrieren.
Die Erkenntnis, dass Sie Prototypen und Experimente benötigen, anstatt gleich mit der Entwicklung von Produktionscode zu beginnen, ist nicht immer einfach und sogar nicht immer möglich. Mit dem soeben erworbenen Wissen können Sie möglicherweise den Bedarf an Prototyping beim nächsten Mal erkennen. Oder vielleicht auch nicht. Aber zumindest wissen Sie jetzt, dass diese Option in Betracht gezogen werden soll. Und das an sich ist wichtiges Wissen.
Das andere Problem ist meiner Meinung nach mit Ihrer Wahrnehmung. Wir alle machen Fehler, und es ist so leicht, im Nachhinein zu sehen, was wir anders hätten machen sollen. So lernen wir. Schreiben Sie Ihre Investition in Komponententests auf, um herauszufinden, wie wichtig Prototyping sein kann, und überstehen Sie dies. Bemühe dich nur, nicht zweimal den gleichen Fehler zu machen :-)
quelle
Der Punkt von TDD ist, dass Sie gezwungen sind, kleine Codeschritte in kleinen Funktionen zu schreiben , um genau dieses Problem zu vermeiden. Wenn Sie wochenlang Code für eine Domain geschrieben haben und jede einzelne von Ihnen geschriebene Dienstprogrammmethode beim Überdenken der Architektur unbrauchbar wird, sind Ihre Methoden mit ziemlicher Sicherheit zu umfangreich. (Ja, mir ist bewusst, dass das jetzt nicht gerade tröstlich ist ...)
quelle
Brooks sagte: "Planen Sie, einen wegzuwerfen; Sie werden es sowieso tun." Mir scheint, dass Sie genau das tun. Das heißt, Sie sollten Ihre Unit-Tests so schreiben, dass sie die Code-Unit testen und nicht so viele Codes. Das sind eher Funktionstests und sollten daher über keine interne Implementierung gehen.
Wenn ich zum Beispiel einen PDE-Löser (Partial Differential Equations) schreiben möchte, würde ich ein paar Tests schreiben, um Dinge zu lösen, die ich mathematisch lösen kann. Das sind meine ersten "Unit" -Tests - lesen Sie: Funktionstests werden als Teil eines xUnit-Frameworks ausgeführt. Diese ändern sich nicht, je nachdem, welchen Algorithmus ich zur Lösung der PDE verwende. Alles, was mich interessiert, ist das Ergebnis. Die zweiten Unit-Tests konzentrieren sich auf die Funktionen, die zur Codierung des Algorithmus verwendet werden, und wären daher algorithmenspezifisch - etwa Runge-Kutta. Wenn ich herausfinden würde, dass Runge-Kutta nicht geeignet ist, hätte ich immer noch diese Tests der obersten Ebene (einschließlich der Tests, die zeigten, dass Runge-Kutta nicht geeignet ist). Somit würde die zweite Iteration immer noch viele der gleichen Tests wie die erste haben.
Ihr Problem liegt vielleicht am Design und nicht unbedingt am Code. Aber ohne weitere Details ist es schwer zu sagen.
quelle
Beachten Sie, dass TDD ein iterativer Prozess ist. Schreiben Sie einen kleinen Test (in den meisten Fällen sollten einige Zeilen ausreichen) und führen Sie ihn aus. Der Test sollte fehlschlagen, arbeiten Sie jetzt direkt an Ihrer Hauptquelle und versuchen Sie, die getestete Funktionalität so zu implementieren, dass der Test bestanden wird. Jetzt nochmal von vorne anfangen.
Sie sollten nicht versuchen, alle Tests auf einmal zu schreiben, da dies, wie Sie bemerkt haben, nicht funktioniert. Dies verringert das Risiko, dass Sie Ihre Zeit damit verschwenden, Tests zu schreiben, die nicht verwendet werden.
quelle
Ich glaube, Sie haben es selbst gesagt: Sie waren sich nicht sicher, wie Sie vor dem Schreiben aller Unit-Tests vorgegangen sind.
Das, was ich gelernt habe, indem ich die TDD-Projekte aus der Praxis, mit denen ich gearbeitet habe (nicht so viele, nur 3, die 2 Jahre Arbeit abdecken), mit dem, was ich theoretisch gelernt habe, verglichen habe, ist das automatisierte Testen! exklusiv).
Mit anderen Worten, das T in TDD muss kein U haben ... Es ist automatisiert, aber weniger ein Komponententest (wie in Testklassen und -methoden) als ein automatisierter Funktionstest: Es ist auf dem gleichen Niveau von funktionaler Granularität als die Architektur, an der Sie gerade arbeiten. Sie beginnen auf hohem Niveau mit wenigen Tests und nur dem funktionalen Gesamtüberblick, und schließlich haben Sie Tausende von UTs und alle Ihre Klassen sind in einer schönen Architektur gut definiert ...
Unit-Tests bieten Ihnen eine große Hilfe bei der Arbeit im Team, um zu vermeiden, dass Codeänderungen zu endlosen Zyklen von Fehlern führen. Aber ich habe noch nie etwas so genaues geschrieben, als ich mit der Arbeit an einem Projekt begonnen habe, bevor ich mindestens einen globalen POC für jede User Story hatte.
Vielleicht ist es nur meine persönliche Art, dies zu tun. Ich habe nicht die nötige Erfahrung, um von Grund auf zu entscheiden, welche Muster oder Strukturen mein Projekt haben wird, also werde ich meine Zeit nicht damit verschwenden, von Anfang an Hunderte von UTs zu schreiben ...
Im Allgemeinen wird die Idee, alles zu zerbrechen und alles zu werfen, immer da sein. So "kontinuierlich" wir auch versuchen können, mit unseren Werkzeugen und Methoden umzugehen, manchmal ist der einzige Weg, Entropie zu bekämpfen, von vorne zu beginnen. Das Ziel ist jedoch, dass in diesem Fall das von Ihnen implementierte automatisierte Testen und Testen von Einheiten Ihr Projekt bereits kostengünstiger macht, als wenn es nicht vorhanden wäre - und wenn Sie das Gleichgewicht finden.
quelle
Die Verschmelzung von Unit-Testing mit testgetriebener Entwicklung ist die Quelle vieler Qualen und Leiden. Lassen Sie es uns noch einmal überprüfen:
Fazit: Unit Testing hat einen Implementierungsfokus, TDD einen Anforderungsfokus. Sie sind nicht dasselbe.
quelle
Testgetriebene Entwicklung soll Ihre Entwicklung vorantreiben . Die Tests, die Sie schreiben, helfen Ihnen, die Richtigkeit des Codes, den Sie gerade schreiben, zu bestätigen und die Entwicklungsgeschwindigkeit ab der ersten Zeile zu erhöhen.
Sie scheinen zu glauben, dass die Tests eine Belastung darstellen und nur für die spätere schrittweise Entwicklung gedacht sind. Diese Denkweise stimmt nicht mit TDD überein.
Vielleicht können Sie es mit statischer Eingabe vergleichen: Obwohl man Code ohne statische Typinformationen schreiben kann, hilft das Hinzufügen von statischem Typ zum Code, bestimmte Eigenschaften des Codes zu bestätigen, den Geist zu befreien und sich stattdessen auf wichtige Strukturen zu konzentrieren, wodurch die Geschwindigkeit und Geschwindigkeit erhöht werden Wirksamkeit.
quelle
Das Problem bei einem größeren Refactoring ist, dass Sie manchmal einem Weg folgen können und werden, der Sie zu der Erkenntnis führt, dass Sie mehr abgebissen haben, als Sie kauen können. Riesen-Refactorings sind ein Fehler. Wenn das Systemdesign in erster Linie fehlerhaft ist, dauert das Refactoring möglicherweise nur so lange, bis Sie eine harte Entscheidung treffen müssen. Lassen Sie das System so, wie es ist, und arbeiten Sie daran herum, oder planen Sie eine Neugestaltung und einige wesentliche Änderungen.
Es geht aber auch anders. Der eigentliche Vorteil des Refactorings von Code besteht darin, dass die Dinge einfacher, leichter zu lesen und noch einfacher zu warten sind. Wenn Sie sich einem Problem nähern, über das Sie unsicher sind, führen Sie eine Änderung durch, gehen Sie so weit, um herauszufinden, wohin sie führen könnte, um mehr über das Problem zu erfahren, werfen Sie dann die Spitze weg und wenden Sie ein neues Refactoring basierend auf der Spitze an hat dich gelehrt. Die Sache ist, Sie können Ihren Code wirklich nur mit Sicherheit verbessern, wenn die Schritte klein sind und Ihre Umgestaltungsbemühungen Ihre Fähigkeit, Ihre Tests zuerst zu schreiben, nicht übersteigen. Die Versuchung besteht darin, einen Test zu schreiben, dann einen Code und dann einen weiteren Code, da eine Lösung möglicherweise naheliegend erscheint. Sie werden jedoch bald feststellen, dass sich durch Ihre Änderung viel mehr Tests ändern werden. Sie müssen also darauf achten, dass Sie jeweils nur eine Änderung vornehmen.
Die Antwort lautet daher: Machen Sie aus Ihrem Refactoring niemals ein großes. Kleine Schritte. Beginnen Sie mit dem Extrahieren von Methoden und versuchen Sie dann, die Duplizierung zu entfernen. Fahren Sie dann mit dem Extrahieren von Klassen fort. Jeweils in winzigen Schritten eine kleine Veränderung nach der anderen. Wenn Sie Code extrahieren, schreiben Sie zuerst einen Test. Wenn Sie Code entfernen, entfernen Sie ihn und führen Sie die Tests aus. Entscheiden Sie, ob die fehlerhaften Tests nicht mehr benötigt werden. Ein winziger Babyschritt nach dem anderen. Es scheint, als würde es länger dauern, aber Ihre Refactoring-Zeit wird sich tatsächlich erheblich verkürzen.
Die Realität ist jedoch, dass jeder Spike scheinbar eine potenzielle Verschwendung von Aufwand ist. Codeänderungen gehen manchmal nirgendwo hin und Sie stellen fest, dass Sie Ihren Code von Ihrem vcs wiederherstellen. Dies ist nur eine Realität dessen, was wir von Tag zu Tag tun. Jeder Spike, der ausfällt, wird jedoch nicht verschwendet, wenn er Ihnen etwas beibringt. Bei jedem fehlgeschlagenen Refactoring werden Sie feststellen, dass Sie entweder zu schnell versuchen, zu viel zu tun, oder dass Ihr Ansatz möglicherweise falsch ist. Auch das ist keine Zeitverschwendung, wenn Sie etwas daraus lernen. Je mehr du dieses Zeug machst, desto mehr lernst du und desto effizienter wirst du dabei. Mein Rat ist, es erst einmal zu tragen, zu lernen, mehr zu tun, indem man weniger tut, und zu akzeptieren, dass dies so ist, wie es wahrscheinlich sein muss, bis man besser erkennt, wie weit man einen Spike nimmt, bevor er Sie nirgendwohin führt.
quelle
Ich bin mir nicht sicher, warum Ihr Ansatz nach 3 Tagen fehlerhaft ausfiel. Abhängig von Ihren Unsicherheiten in Ihrer Architektur können Sie Ihre Teststrategie ändern:
Wenn Sie sich bezüglich der Leistung unsicher sind, möchten Sie vielleicht mit ein paar Integrationstests beginnen, die die Leistung bestätigen?
Wenn Sie sich mit der Komplexität von APIs befassen, schreiben Sie einige wirklich bloße Tests mit kleinen Einheiten, um herauszufinden, wie Sie am besten vorgehen können. Sie müssen nichts implementieren, sondern Ihre Klassen müssen nur fest codierte Werte zurückgeben oder NotImplementedExceptions auslösen.
quelle
Für mich sind Unit-Tests auch eine Gelegenheit, das Interface unter "echten" Einsatz zu stellen (na ja, so echt wie Unit-Tests!).
Wenn ich gezwungen bin, einen Test durchzuführen, muss ich mein Design anwenden. Dies hilft, die Dinge vernünftig zu halten (wenn etwas so komplex ist, dass das Schreiben eines Tests eine Belastung darstellt, wie wird es sein, es zu verwenden?).
Dies vermeidet keine Änderungen im Design, sondern macht deren Notwendigkeit deutlich. Ja, ein komplettes Umschreiben ist ein Schmerz. Um dies zu vermeiden, habe ich normalerweise (einen oder mehrere) Prototypen eingerichtet, möglicherweise in Python (mit der endgültigen Entwicklung in c ++).
Zugegeben, Sie haben nicht immer die Zeit für all diese Leckereien. Das sind genau die Fälle , wenn Sie eine benötigen GRÖSSER Menge Zeit , um Ihre Ziele zu erreichen ... und / oder alles unter Kontrolle zu halten.
quelle
Willkommen im Zirkus der kreativen Entwickler .
Anstatt alle 'legalen / vernünftigen' Codierungsmethoden am Anfang zu respektieren,
versuchen Sie es mit Intuition , vor allem, wenn es für Sie wichtig und neu ist und wenn kein Beispiel so aussieht, wie Sie es möchten:
- Schreiben Sie mit Ihrem Instinkt, aus Dingen, die Sie bereits kennen , nicht mit deinem Verstand und deiner Vorstellungskraft.
- Und stoppen.
- Nehmen Sie eine Lupe und überprüfen Sie alle Wörter, die Sie schreiben: Sie schreiben "Text", weil "Text" in der Nähe von String ist, aber "Verb", "Adjektiv" oder etwas genaueres benötigt wird. Lesen Sie es erneut und stellen Sie die Methode mit neuem Sinn ein
. .. oder, Sie haben einen Code geschrieben, der über die Zukunft nachdenkt? Entfernen Sie es
- Korrigieren Sie, erledigen Sie andere Aufgaben (Sport, Kultur oder andere Dinge außerhalb des Geschäfts), kommen Sie zurück und lesen Sie noch einmal.
- Alles passt gut,
- Korrigieren Sie, erledigen Sie eine andere Aufgabe, kommen Sie zurück und lesen Sie noch einmal.
- Alle passen gut zusammen, gehen Sie zu TDD.
- Jetzt ist alles in Ordnung, gut
.
Was erscheint:
- Sie haben einen Code geschrieben, der alle Regeln einhält
- Sie bekommen eine Erfahrung, eine neue Art zu arbeiten,
- etwas , was sich in Ihren Gedanken ändert, Sie werden sich niemals vor einer neuen Konfiguration fürchten.
Und jetzt, wenn Sie eine UML sehen, die so aussieht, können Sie sagen:
"Boss, ich beginne mit TDD dafür ..." Ist das eine
andere neue Sache?
"Boss, ich würde etwas ausprobieren, bevor ich beschließe, wie ich codiere." Mit
freundlichen Grüßen von PARIS
Claude
quelle