Ich versuche, mich mit TDD zu beschäftigen, insbesondere mit dem Entwicklungsteil. Ich habe mir einige Bücher angesehen, aber die, die ich gefunden habe, befassen sich hauptsächlich mit dem Testteil - der Geschichte von NUnit, warum das Testen gut ist, Rot / Grün / Refaktor und wie man einen Zeichenkettenrechner erstellt.
Gutes Zeug, aber das ist "nur" Unit Testing, nicht TDD. Insbesondere verstehe ich nicht, wie TDD mir hilft, ein gutes Design zu erhalten, wenn ich ein Design benötige, um es zu testen.
Stellen Sie sich zur Veranschaulichung diese drei Anforderungen vor:
- Ein Katalog muss eine Produktliste enthalten
- Der Katalog sollte sich merken, welche Produkte ein Benutzer angesehen hat
- Benutzer sollten in der Lage sein, nach einem Produkt zu suchen
An diesem Punkt ziehen viele Bücher ein magisches Kaninchen aus dem Hut und tauchen nur in "Testing the ProductService" ein, aber sie erklären nicht, wie sie zu dem Schluss gekommen sind, dass es überhaupt einen ProductService gibt. Das ist der "Entwicklungsteil" in TDD, den ich zu verstehen versuche.
Es muss ein vorhandenes Design geben, aber Dinge außerhalb von Entity-Services (das heißt: Es gibt ein Produkt, also sollte es einen ProductService geben) sind nirgends zu finden (z. B. erfordert die zweite Anforderung, dass ich ein Konzept von a habe Benutzer, aber wo sollte ich die Funktionen zur Erinnerung hinstellen? Und ist Suche eine Funktion des ProductService oder ein separater SearchService? Woher sollte ich wissen, welche ich wählen soll?)
Laut SOLID würde ich einen UserService benötigen, aber wenn ich ein System ohne TDD entwerfe, könnte es sein, dass ich eine ganze Reihe von Single-Method Services bekomme. Soll ich mit TDD nicht erst mein Design entdecken?
Ich bin ein .NET-Entwickler, aber Java-Ressourcen würden auch funktionieren. Ich habe das Gefühl, dass es anscheinend keine echte Beispielanwendung oder ein Buch gibt, die bzw. das sich mit einer echten Branchenanwendung befasst. Kann jemand ein klares Beispiel liefern, das den Prozess der Erstellung eines Designs mit TDD veranschaulicht?
Antworten:
Die Idee von TDD ist, mit dem Testen zu beginnen und daran zu arbeiten. Wenn Sie also ein Beispiel für "Ein Katalog muss eine Liste von Produkten enthalten" nehmen, bedeutet dies, dass der Test "Nach Produkten im Katalog suchen" lautet, und dies ist somit der erste Test. Was beinhaltet ein Katalog? Was beinhaltet ein Produkt? Dies sind die nächsten Teile, und die Idee ist, einige Teile zusammenzufügen, die so etwas wie ein ProductService sind, der entsteht, wenn dieser erste Test bestanden wird.
Die Idee von TDD ist, mit einem Test zu beginnen und dann den Code zu schreiben, der diesen Test als ersten Punkt bestehen lässt. Komponententests sind Teil dieses Ja, aber Sie sehen sich nicht das Gesamtbild an, das sich aus Tests und dem anschließenden Schreiben des Codes zusammensetzt, damit es an dieser Stelle keine blinden Flecken gibt, da noch kein Code vorhanden ist.
Test Driven Development Tutorial, in dem die Folien 20-22 die wichtigsten sind. Die Idee ist, zu wissen, was die Funktionalität als Ergebnis haben soll, einen Test dafür zu schreiben und dann eine Lösung zu erstellen. Das Designteil kann variieren, je nachdem, was erforderlich ist oder nicht. Ein wichtiger Punkt ist, TDD von Anfang an zu verwenden, anstatt zu versuchen, spät in ein Projekt einzuführen. Wenn Sie zuerst mit Tests beginnen, kann dies hilfreich sein und ist in gewissem Sinne erwähnenswert. Wenn Sie versuchen, die Tests später hinzuzufügen, wird dies möglicherweise verschoben oder verzögert. Die späteren Folien können ebenfalls nützlich sein.
Ein Hauptvorteil von TDD ist, dass Sie zu Beginn der Tests nicht an ein Design gebunden sind. Daher besteht die Idee darin, die Tests zu erstellen und den Code zu erstellen, der diese Tests als Entwicklungsmethode besteht. Ein Big Design Up Front kann zu Problemen führen, da dadurch die Idee entsteht, Dinge zu verriegeln, wodurch das System letztendlich weniger flink gebaut wird.
Robert Harvey fügte dies in den Kommentaren hinzu, die es wert sind, in der Antwort erwähnt zu werden:
quelle
TDD hilft mir, schneller zum besten Design zu kommen, als TDD nicht. Ich würde wahrscheinlich mit oder ohne zum besten Design kommen. Aber die Zeit, die ich damit verbracht hätte, darüber nachzudenken und ein paar Male in den Code einzutauchen, wird stattdessen damit verbracht, Tests zu schreiben. Und es ist weniger Zeit. Für mich. Nicht für jeden. Und selbst wenn es genauso lange dauern würde, würde ich eine Reihe von Tests durchführen müssen, damit das Refactoring sicherer und der Code auf der ganzen Linie noch besser wird.
Wie macht es das?
Erstens ermutigt es mich, über jede Klasse als Dienst für einen Clientcode nachzudenken. Besserer Code entsteht, wenn man sich überlegt, wie der aufrufende Code die API verwenden soll, anstatt sich Gedanken darüber zu machen, wie der Code selbst aussehen soll.
Zweitens hindert es mich daran, viel zu viel zyklometische Komplexität in eine Methode zu schreiben, während ich darüber nachdenke. Jeder zusätzliche Weg durch eine Methode wird dazu neigen, die Anzahl der Tests, die ich durchführen muss, zu verdoppeln. Bloße Faulheit diktiert, dass, nachdem ich zu viel Logik hinzugefügt habe und 16 Tests schreiben muss, um eine Bedingung hinzuzufügen, es Zeit ist, einige davon in eine andere Methode / Klasse zu ziehen und sie separat zu testen.
Es ist wirklich so einfach. Es ist kein magisches Designwerkzeug.
quelle
Diese Anforderungen sollten in menschlicher Hinsicht angepasst werden. Wer möchte wissen, welche Produkte der Benutzer zuvor angesehen hat? Der Benutzer? Ein Verkäufer?
Wie? Namentlich? Nach Marke? Der erste Schritt in der testgetriebenen Entwicklung besteht darin, einen Test zu definieren, zum Beispiel:
>
Wenn dies die einzigen Voraussetzungen sind, würde ich auf keinen Fall einen Produktservice erstellen. Ich könnte eine sehr einfache Webseite mit einer statischen Produktliste erstellen. Das würde perfekt funktionieren, bis Sie die Anforderungen zum Hinzufügen und Löschen von Produkten erfüllen. An diesem Punkt könnte ich entscheiden, dass es am einfachsten ist, eine relationale Datenbank und einen ORM zu verwenden und eine Produktklasse zu erstellen, die einer einzelnen Tabelle zugeordnet ist. Immer noch kein ProductService. Klassen wie ProductService werden erstellt, wenn und wenn sie benötigt werden. Möglicherweise müssen mehrere Webanforderungen dieselben Abfragen oder Aktualisierungen ausführen. Anschließend wird die ProductService-Klasse erstellt, um das Kopieren von Code zu verhindern.
Zusammenfassend lässt sich sagen, dass TDD den zu schreibenden Code steuert. Der Entwurf erfolgt, wenn Sie Implementierungsentscheidungen treffen und den Code dann in Klassen umgestalten, um Duplikationen zu vermeiden und Abhängigkeiten zu steuern. Wenn Sie Code hinzufügen, müssen Sie neue Klassen erstellen, um den Code SOLID beizubehalten. Sie müssen sich jedoch nicht im Voraus entscheiden, ob Sie eine Produktklasse und eine ProductService-Klasse benötigen. Sie können feststellen, dass das Leben mit nur einer Produktklasse vollkommen in Ordnung ist.
quelle
ProductService
dann. Aber wie hat TDD Ihnen gesagt, dass Sie eine Datenbank und einen ORM benötigen?Andere mögen anderer Meinung sein, aber für mich basieren viele der neueren Methoden auf der Annahme, dass der Entwickler den größten Teil dessen tun wird, was die älteren Methoden aus Gewohnheit oder persönlichem Stolz formuliert haben für sie, und die Arbeit ist in einer sauberen Sprache oder die saubereren Teile einer etwas chaotischen Sprache gekapselt, so dass Sie alle Testgeschäfte erledigen können.
Einige Beispiele, bei denen ich in der Vergangenheit darauf gestoßen bin:
Nehmen Sie ein paar Spezialunternehmen und sagen Sie ihnen, dass ihr Team Agile und Test First ist. Sie haben oft keine andere Angewohnheit, als nach Vorgabe zu arbeiten, und sie kümmern sich nicht um die Qualität der Arbeit, solange diese lange genug dauert, um das Projekt abzuschließen.
Versuchen Sie, zuerst einen neuen Test durchzuführen, und verbringen Sie einen Großteil Ihrer Zeit damit, Tests zu rippen, da Sie feststellen, dass verschiedene Ansätze und Schnittstellen Mist sind.
Codieren Sie etwas Niedriges und geben Sie entweder eine Ohrfeige wegen mangelnder Abdeckung oder schreiben Sie eine Reihe von Tests, die nicht viel wert sind, weil Sie das zugrunde liegende Verhalten, an das Sie gebunden sind, nicht verspotten können.
Wenn Sie TDD machen und es für Sie funktioniert, ist es gut für Sie, aber es gibt eine Menge Dinge (ganze Jobs oder Phasen eines Projekts), bei denen dies einfach keinen Mehrwert bringt.
Ihr Beispiel hört sich so an, als wären Sie noch nicht einmal mit einem Entwurf befasst. Entweder müssen Sie sich mit der Architektur befassen, oder Sie erstellen einen Prototyp. Meiner Meinung nach müssen Sie zuerst einiges davon durchstehen.
quelle
Ich bin überzeugt, dass TDD ein sehr wertvoller Ansatz für das detaillierte Design des Systems ist - dh die APIs und das Objektmodell. Um jedoch zu dem Punkt in einem Projekt zu gelangen, an dem Sie anfangen würden, TDD zu verwenden, müssen Sie das Gesamtbild des Entwurfs in gewisser Weise bereits modelliert haben und das Gesamtbild der Architektur in gewisser Weise bereits modelliert haben. @ user414076 paraphrasiert, dass Robert Martin eine Designidee hat, aber nicht damit verheiratet ist. Genau. Fazit - TDD ist nicht die einzige Designaktivität, sondern die Art und Weise, wie die Details des Designs präzisiert werden. TDD müssen andere Entwurfsaktivitäten vorausgehen und zu einem Gesamtansatz (wie Agile) passen, der angibt, wie der Gesamtentwurf erstellt und weiterentwickelt wird.
Zu Ihrer Information - zwei Bücher, die ich zu diesem Thema empfehle und die konkrete und realistische Beispiele geben:
Wachsende objektorientierte Software, geführt von Tests - erklärt und gibt ein vollständiges Projektbeispiel. Dies ist ein Buch über Design, nicht Testen . Das Testen wird verwendet, um das erwartete Verhalten während der Entwurfsaktivitäten zu spezifizieren.
Testgetriebene Entwicklung Ein praktischer Leitfaden - eine langsame und schrittweise Anleitung zur Entwicklung einer vollständigen, wenn auch kleinen App.
quelle
TTD treibt die Designentdeckung durch Testfehler an, nicht durch Erfolg. Daher können Sie Unbekannte testen und iterativ erneut testen, da Unbekannte letztendlich einer vollständigen Reihe von Komponententests ausgesetzt sind - eine sehr schöne Sache für die laufende Wartung und eine sehr schwierige Sache, die Sie ausprobieren können Nachrüstung nach dem Schreiben / Freigeben des Codes.
Beispielsweise kann eine Anforderung sein, dass die Eingabe in verschiedenen Formaten erfolgen kann, von denen noch nicht alle bekannt sind. Mit TDD würden Sie zuerst einen Test schreiben, der sicherstellt, dass die entsprechende Ausgabe bei jedem Eingabeformat bereitgestellt wird . Offensichtlich schlägt dieser Test fehl. Schreiben Sie Code, um die bekannten Formate zu verarbeiten, und wiederholen Sie den Test. Da die unbekannten Formate durch das Sammeln von Anforderungen verfügbar gemacht werden, werden neue Tests geschrieben, bevor der Code geschrieben wird. Diese sollten ebenfalls fehlschlagen. Dann wird neuer Code geschrieben, um die neuen Formate zu unterstützen, und alle Tests werden wiederholt, um die Wahrscheinlichkeit einer Regression zu verringern.
Es ist auch hilfreich, sich einen Geräteausfall als "unvollendeten" Code anstelle eines "kaputten" Codes vorzustellen. TDD ermöglicht unfertige Einheiten (erwartete Ausfälle), verringert jedoch das Auftreten von defekten Einheiten (unerwartete Ausfälle).
quelle
In der Frage heißt es:
Sie kamen zu diesem Schluss, indem sie darüber nachdachten, wie sie dieses Produkt testen würden. "Was für ein Produkt macht das?" "Nun, wir könnten einen Dienst schaffen". "Ok, lass uns einen Test für einen solchen Service schreiben"
quelle
Eine Funktionalität kann viele Designs haben und TDD sagt Ihnen nicht vollständig, welche die beste ist. Selbst wenn Sie durch Tests modulareren Code erstellen können, können Sie auch Module erstellen, die den Testanforderungen und nicht der Realität in der Produktion entsprechen. Sie müssen also verstehen, wohin Sie gehen und wie die Dinge in das Gesamtbild passen sollten. Anders ausgedrückt, es gibt funktionale und nicht funktionale Anforderungen. Vergessen Sie nicht die letzte.
Bezüglich des Designs verweise ich auf Bücher von Robert C. Martin (Agile Development), aber auch auf Martin Fowlers Patterns of Enterprise Application Architecture und Domain Driver Design. Letzteres ist besonders dann sehr systematisch, wenn es darum geht, die Entitäten und Beziehungen aus den Anforderungen zu extrahieren.
Wenn Sie dann ein gutes Gefühl für die verfügbaren Optionen zur Verwaltung dieser Entitäten haben, können Sie Ihren TDD-Ansatz unterstützen.
quelle
Nein.
Wie können Sie etwas testen, das Sie nicht zuerst entworfen haben?
Dies sind keine Anforderungen, dies sind Definitionen von Daten. Ich weiß nicht, was das Geschäft mit Ihrer Software ist, aber es ist unwahrscheinlich, dass Analysten so sprechen.
Sie müssen wissen, was die Invarianten Ihres Systems sind.
Eine Anforderung wäre etwa:
Wenn dies die einzige Voraussetzung ist, haben Sie möglicherweise eine Klasse wie:
Dann würden Sie mit TDD einen Testfall schreiben, bevor Sie die order () -Methode implementieren.
Wenn der zweite Test fehlschlägt, können Sie die order () -Methode wie gewünscht implementieren.
quelle
Sie sind ganz richtig. TDD führt zu einer guten Implementierung eines bestimmten Designs. Es wird Ihrem Designprozess nicht helfen.
quelle
TDD hilft viel, jedoch spielt die Softwareentwicklung eine wichtige Rolle. Der Entwickler sollte sich den Code anhören, der gerade geschrieben wird. Refactoring ist der dritte Teil des TDD-Zyklus. Dies ist der Hauptschritt, bei dem sich der Entwickler konzentrieren und überlegen sollte, bevor er zum nächsten roten Test übergeht. Gibt es eine Vervielfältigung? Werden SOLID-Prinzipien angewendet? Was ist mit hoher Kohäsion und geringer Kopplung? Was ist mit Namen? Schauen Sie sich den Code, der aus den Tests hervorgeht, genauer an und prüfen Sie, ob etwas geändert und neu gestaltet werden muss. Frage den Code und der Code sagt dir, wie er gestaltet werden soll. Normalerweise schreibe ich Sätze von mehreren Tests, untersuche diese Liste und erstelle ein einfaches Design. Es muss nicht "final" sein, normalerweise nicht, weil es sich ändert, wenn neue Tests hinzugefügt werden. Hier kommt das Design.
quelle