TDD, neue Tests, alte noch nicht implementiert

13

Ich experimentiere mit testgetriebener Entwicklung und habe festgestellt, dass ich häufig in folgende Situationen gerate:

  1. Ich schreibe Tests für einige Funktionen X. Diese Tests schlagen fehl.
  2. Beim Versuch, X zu implementieren, sehe ich, dass ich ein Feature Y in einer unteren Ebene meines Codes implementieren muss. So...
  3. Ich schreibe Tests für Y. Jetzt schlagen beide Tests für X und Y fehl.

Einmal wurden 4 Features in verschiedenen Codeebenen gleichzeitig bearbeitet, und ich verlor den Fokus auf das, was ich gerade tue (zu viele Tests schlagen gleichzeitig fehl).

Ich denke, ich könnte dieses Problem lösen, indem ich mehr Aufwand in die Planung meiner Aufgaben stecke, noch bevor ich anfange, Tests zu schreiben. Aber in einigen Fällen wusste ich nicht, dass ich tiefer gehen muss, weil ich zB die API der unteren Schicht nicht sehr gut kannte.

Was soll ich in solchen Fällen tun? Hat TDD irgendwelche Empfehlungen?

Liori
quelle

Antworten:

9

Das Gute ist, dass Sie erkennen, dass Ihr zu testender Code Unterstützung benötigt. Erstellen Sie eine Schnittstelle und stellen Sie mithilfe von Mocks sicher, dass Ihre Tests den richtigen Code kennzeichnen, anstatt sie sofort zu implementieren. Nachdem Sie diese Tests bestanden haben, können Sie mit der Implementierung des Codes fortfahren, auf den sie sich stützen.

Michael Brown
quelle
Meine Tests wissen normalerweise nicht, was eine Methode intern tun soll (z. B. welche API auf niedrigerer Ebene aufgerufen werden soll). Sollte ich nur die Tests anpassen, um alles zu verspotten, was ich im getesteten Code benötige?
Liori
2
Ebenso sollte es Ihren getesteten Klassen egal sein, was "untere Schichten" tun. Verwenden Sie Mocks / Stubs anstelle der tatsächlichen Klassen / Objekte. Dies erfordert möglicherweise etwas mehr Designaufwand, führt jedoch zu Code, der weniger gekoppelt und einfacher wiederzuverwenden ist.
Mchl
1
Verwenden Sie Abhängigkeitsinjektion? Auf diese Weise können Sie einfach Probleme auf niedrigerer Ebene von Klassen auf höherer Ebene trennen. Ihre zu testende Klasse verfügt über einen Konstruktor mit Parametern für ihre Abhängigkeiten (als Schnittstellen). In Ihrem Test erstellen Sie Mocks für die Schnittstellen. Grundsätzlich geben Sie vor, die untergeordneten Dienste bereits implementiert zu haben.
Michael Brown
@ Mike Brown, ja, das tue ich. Ich weiß, dass ich Scheinobjekte erstellen kann. Aber dann in meinem Test für FeatureX wissen, welchen Teil der Abhängigkeiten Xich verspotten muss. Ich bin der Meinung, dass dies Teil der Implementierungsdetails ist, die nicht Teil der Tests sein sollten. Andernfalls muss ich möglicherweise die Tests ändern, während die Implementierung überarbeitet wird. Sollte ich mir darüber Sorgen machen?
Liori
1
Überhaupt nicht ... Die Tests sollten die Annahmen des getesteten Systems widerspiegeln. Es hilft Ihnen auch dabei, aufzuzeigen, was Sie von den Diensten benötigen, auf die sich das System stützt. Ich habe Ihnen in dieser Sache immer zugestimmt, aber ich vergleiche es damit, wie ich rekursives Programmieren verstehe. Zuerst schreiben Sie den Code unter der Annahme, dass Sie eine Funktion haben, die das tut, was Sie wollen. Dann schreiben Sie den Code, der macht, was Sie wollen.
Michael Brown
4

Stubs und Mocks können verwendet werden, um die Funktionalität zu simulieren, die noch nicht geändert / implementiert wurde. Sie können Ihnen auch dabei helfen, die Abhängigkeiten aufzulösen, die diese Art von Kettenreaktion verursachen.

Auf der anderen Seite ist es am besten, nur einen (nicht bestandenen) Test durchzuführen, der die nächste Änderung bewirkt.

Andere Tests, die auf den Code abzielen, der auf neuen Funktionen basiert, können vorübergehend deaktiviert werden, da sie zu diesem Zeitpunkt nicht wirklich relevant sind, d. H. Deaktivieren Sie in Ihrem Fall die Tests für X, bis Sie Y usw. implementieren.

Auf diese Weise können Sie sich nur auf die nächste Änderung konzentrieren, die Sie möchten, denke ich.

Ratkok
quelle
Ha, ich habe nach einer Funktion gesucht, mit der ein Test während eines Testlaufs in meiner IDE deaktiviert werden kann, und habe keine gefunden. Jetzt habe ich festgestellt, dass Python unittestbereits Test-Skipping hat. Das könnte für mich reichen.
Liori
Wir verwenden das Google Test C ++ Framework - und es gibt eine Option zum Deaktivieren von Tests. Deaktivierte Tests werden nicht ausgeführt, sondern kompiliert - sobald Sie sie benötigen - und sind sofort einsatzbereit (außerdem können Sie die Ausführung deaktivierter Tests erzwingen - Art der Laufzeitaktivierung) - hervorragende Funktion ...
ratkok
3

Halt

Auf den ersten Blick scheint es hier zwei verschiedene Probleme zu geben:

  1. Sie haben einige Storys und Testszenarien vergessen und erst entdeckt, als Sie mit der Arbeit an einem bestimmten Testszenario begonnen haben, und / oder

  2. Sie tun wirklich Testeinheit und TDD nicht Feature - Tests

Für 1, Halten Sie gehen Sie zurück und aktualisieren Sie die Storys und Testszenarien. Beginnen Sie dann mit einem anderen Szenario.

Halten Sie bei # 2 an und denken Sie daran, dass Sie Funktionen und nicht Einheiten testen. Verwenden Sie daher Mocks, um andere Schnittstellen zu beschönigen und / oder implementieren Sie mehr Code, um den Test zu bestehen, ohne neue Testszenarien hinzuzufügen. Dies setzt voraus, dass Sie keine Testszenarien verpassen, sondern stattdessen - und das ist wirklich üblich - Unit-Tests und TDD zusammenführen.

Steven A. Lowe
quelle
Ihre Antwort gefällt mir sehr, sie erklärt besser, was wirklich vor sich geht.
maple_shaft
... Vor diesem Hintergrund kenne ich keinen Premierminister der Welt, der bei dem Satz "STOP, wir müssen zurückgehen" nicht den Verstand verlieren wird. Sie werden alles nur Mögliche tun, um ihre Erstgeborenen am Altar zu opfern, um das Projekt voranzutreiben, technische Schulden zu machen und unvollständige Komponententests zu verhindern. Ich vermute, Sie können es ihnen nicht verübeln, wenn ihre einzige Messgröße in einer Organisation darin besteht, das Projekt pünktlich fertig zu stellen. Einige Organisationen legen Wert auf Zeit und vor allem auf Qualität. Aus diesem Grund habe ich TDD wahrscheinlich noch nie erfolgreich in solchen Organisationen gesehen. Leider sind dies die MEISTEN.
maple_shaft
@maple_shaft: Die Zeitspanne, in der Sie anhalten, um sich neu zu gruppieren, kann nur einige Stunden betragen - es sei denn, Ihr Prozess ist weit entfernt von der Ausgangsbasis Das Projekt wird erfolgreich sein. Es hat keinen Sinn, den falschen Weg mit Volldampf einzuschlagen!
Steven A. Lowe
0

Dies ist eine großartige Frage und eine GROSSE Enttäuschung auch für mich bei TDD. Ich glaube, TDD fehlt in diesem Szenario, in dem Sie einfach nicht wissen können, welche Komponenten oder Funktionen auf niedrigerer Ebene Sie benötigen, bis Sie mit der Entwicklung beginnen.

Persönlich fand ich, dass TDD nur funktioniert, wenn Sie genau wissen, was Sie tun müssen und was Sie aufrufen müssen, um eine Funktion auszuführen. Die Entwickler wissen nicht immer alles, bevor wir anfangen. Daher habe ich festgestellt, dass dies der beste Weg für mich ist, um genau die von Ihnen beschriebene Situation zu mildern:

Prototyp

Wenn ich einfache Prototyp-Apps erstelle, um Methoden und Ansätze für ein technisches Problem zu erforschen und zu entdecken, entdecke ich einen Großteil der Vorarbeiten und räume diese Recherche aus dem Weg, bevor ich anfange. Das Entwerfen und Schätzen wird ebenfalls viel einfacher.

Wenn der Prototyp so involviert sein muss, dass er zur Anwendung wird, dann fordere ich Sie dringend auf, das Faule nicht zu tun und anschließend Komponententests für Ihren Prototyp zu erstellen.

Sie sollten zu diesem Zeitpunkt mehr über die API der niedrigeren Ebene wissen und in der Lage sein, die API der niedrigeren Ebene in Ihren Komponenten der höheren Ebene erfolgreich zu verspotten.

maple_shaft
quelle
Sie schlagen also tatsächlich vor, mehr Informationen für die Planungsphase zu erhalten, indem Sie eine explorative Codierung auf informelle Weise (dh ohne eine formalisierte Methodik) durchführen. Und dann nehmen wir an, dass es genügend Informationen gibt, um den tatsächlichen Code zu planen. Habe ich recht?
Liori
Warum nehmen Sie an, dass Prototyping ein informeller Prozess ist? Jede Schätzung sollte Prototyping berücksichtigen und Projektpläne sollten dies sowie eine notwendige Entwicklungsaufgabe berücksichtigen. Ich sehe es genauso wie Design oder Code-Review. In diesem Sinne ist es formalisiert und sollte berücksichtigt werden, noch mehr bei Aufgaben mit vielen Unbekannten. Ohne Prototyping und die Fähigkeit, Proof-of-Concept durchzuführen, setzt TDD lediglich voraus, dass Entwickler ALLES über ALLES mit ALLEN Funktionen wissen. Die reale Welt funktioniert nicht so und es ist mir egal, wie intelligent oder erfahren Sie sind.
maple_shaft
Mit "informeller Art" meinte ich nicht, dass die Zeit für das Prototyping nicht berücksichtigt werden sollte, aber während Sie Prototypen erstellen, folgen Sie weder TDD noch einer anderen Codemethode.
Liori
TDD ist eine Methodik für Unit Testing und Entwicklung. Wäre es sinnvoll, TDD für die Codeüberprüfung durchzuführen? Ist TDD sinnvoll für Design, technische Spezifikationen oder Whiteboarding? Das Prototyping ist eine eigenständige Aufgabe, eine explorative Art der Entwicklung für Forschung, Proof of Concept und Bildung.
maple_shaft
1
TDD macht perfekten Sinn für Prototyping. Es ermöglicht Ihnen, die Dinge, die Sie benötigen (Objekt, Funktion, API, gesamtes Programm), schnell in Form eines wiederholbaren, ausführbaren Anforderungssatzes aufzudecken. Tun Sie sich selbst einen Gefallen und lesen Sie anhand von Tests wachsende objektorientierte Software . Es führt Sie Schritt für Schritt durch die Erstellung einer gesamten Anwendung (einschließlich Integration) in einer Test-First-Weise.
Frank Shearar
0

Es hängt davon ab, welche Art von Tests Sie beim TDD durchführen.

Das klassische Modell besteht darin, Komponententests zu schreiben und Mocks oder Stubs zu verwenden, um den Test von den anderen "Einheiten" des Codes zu entkoppeln.

Es gibt viele andere alternative Modelle wie ATDD, bei denen der Test ein voller Stapel oder ein fast voller Stapel ist. In diesem speziellen Fall stellen Ihre Schreibtests, die das erforderliche Programmverhalten bestätigen, keine einzige Codeeinheit dar, sodass Sie keine anderen Tests schreiben würden. Sie würden das Gerät die Rundreise bekommen, um den Test zu erfüllen. Anschließend fügen Sie weitere Tests für andere Funktionen / Verhalten hinzu.

dietbuddha
quelle