Warum funktioniert TDD? [geschlossen]


92

Testgetriebene Entwicklung (TDD) ist heutzutage groß. Ich sehe es oft als eine Lösung für eine Vielzahl von Problemen hier in der Programmers SE und in anderen Veranstaltungsorten. Ich frage mich, warum es funktioniert.

Aus technischer Sicht ist es mir aus zwei Gründen ein Rätsel:

  1. Der Ansatz "Write Test + Refactor Till Pass" sieht unglaublich anti-engineering aus. Wenn Bauingenieure diesen Ansatz beispielsweise für den Brückenbau oder Autodesigner für ihre Autos verwenden würden, würden sie ihre Brücken oder Autos mit sehr hohen Kosten umgestalten, und das Ergebnis wäre ein Flickwerk ohne durchdachte Architektur . Die "Refactor-till-Pass" -Richtlinie wird oft als Auftrag verstanden, die architektonische Gestaltung zu vergessen und alles Notwendige zu tun , um dem Test zu entsprechen. Mit anderen Worten, der Test und nicht der Benutzer legt die Anforderung fest. Wie können wir in dieser Situation gute "Fähigkeiten" in den Ergebnissen garantieren, dh ein Endergebnis, das nicht nur korrekt, sondern auch erweiterbar, robust, benutzerfreundlich, zuverlässig, sicher usw. ist? Das ist es, was Architektur normalerweise tut.
  2. Tests können nicht garantieren, dass ein System funktioniert. es kann nur zeigen, dass dies nicht der Fall ist. Mit anderen Worten, Tests können zeigen, dass ein System Fehler enthält, wenn ein Test nicht bestanden wird. Ein System, das alle Tests besteht, ist jedoch nicht sicherer als ein System, das diese nicht bestanden hat. Testabdeckung, Testqualität und andere Faktoren sind hier entscheidend. Die falschen Sicherheitsgefühle, die ein "all green" -Ergebnis für viele Menschen hervorruft, wurden in der Zivil- und Luftfahrtindustrie als äußerst gefährlich eingestuft, da dies als "das System ist in Ordnung" interpretiert werden kann, wenn es wirklich bedeutet, dass das System so gut ist als unsere Teststrategie ". Oft wird die Teststrategie nicht überprüft. Oder wer testet die Tests?

Zusammenfassend bin ich mehr besorgt über das "gesteuerte" Bit in TDD als über das "Test" Bit. Testen ist vollkommen in Ordnung; Was ich nicht verstehe, ist, das Design damit voranzutreiben.

Ich würde gerne Antworten erhalten, die Gründe dafür enthalten, warum TDD in der Softwareentwicklung eine gute Praxis ist und warum die oben erläuterten Probleme im Fall von Software nicht relevant (oder nicht relevant genug) sind. Danke.


53
Brücken, Autos und andere physische Konstruktionen sind bei weitem nicht so formbar wie Software. Dies ist ein wichtiger Unterschied und bedeutet, dass Vergleiche zwischen Software und realem Engineering nicht immer relevant sind. Was für Bridges funktioniert, funktioniert möglicherweise nicht für Software und umgekehrt.
Lars Wirzenius

9
Ich stimme Ihren Zweifeln einigermaßen zu. Ich muss zum Beispiel zugeben, dass ich den Eindruck habe, dass eine Testsuite als Nebeneffekt eine etwas "mildere" Aufmerksamkeit beim Schreiben von Code haben kann. Natürlich sind Tests eine gute Sache (eine obligatorische, wenn Sie die Möglichkeit zur Umgestaltung haben möchten), aber nur, wenn sie die Aufmerksamkeit auf Details, Grenzfälle, Effizienz oder Erweiterbarkeit ergänzen und nicht, wenn sie diese ersetzen.
6502

2
@ 6502: Auf jeden Fall! TDD ist kein Wundermittel und wird nicht alle Probleme lösen, die während der Softwareentwicklung auftreten. Es ist jedoch eine nützliche Methode zur Organisation des Workflows. Sie können beispielsweise festlegen, dass alle Grenzfälle durch Tests abgedeckt werden. Sie müssen noch wissen, um welche Grenzfälle es sich handelt, aber jetzt haben Sie auch ein Tool, mit dem Sie überprüfen können, ob Ihr Code sie korrekt verarbeitet.
Mchl

2
@CesarGon, Sie könnten auch an dieser Frage interessiert sein , die ich vor einiger Zeit auf SO gestellt habe ... Nicht ganz TDD, aber verwandt ... Einige sehr aufschlussreiche Antworten.
AviD 30.01.11

6
Wie erstaunlich, dass ein Analogon für Tiefbau / Softwareentwicklung nicht hält. In der gleichen Richtung habe ich oft bemerkt, dass ich Pfannkuchen nicht so kochen kann, wie ich meinen Rasen mähe.

Antworten:


66

Ich denke, hier gibt es ein Missverständnis. Beim Softwaredesign kommt das Design dem Produkt sehr nahe. Im Tiefbau, der Architektur, ist das Design vom eigentlichen Produkt entkoppelt: Es gibt Entwürfe, die das Design enthalten, die dann in das fertige Produkt verwirklicht werden und die durch einen enormen Zeit- und Arbeitsaufwand voneinander getrennt werden.

TDD testet das Design. Aber jedes Autodesign und jedes Gebäudedesign wird auch getestet. Die Konstruktionstechniken werden zuerst berechnet, dann im kleineren Maßstab getestet und dann im größeren Maßstab getestet, bevor sie in einem realen Gebäude ausgeführt werden. Als sie zum Beispiel H-Träger und die Last erfanden, stellten sie sicher, dass dies schon versucht wurde und noch einmal versucht wurde, bevor sie tatsächlich die erste Brücke damit bauten.

Entwürfe von Autos werden ebenfalls getestet, indem Prototypen entworfen werden, und zwar indem Dinge, die nicht genau richtig sind, angepasst werden, bis sie den Erwartungen entsprechen. Ein Teil dieses Prozesses ist jedoch langsamer, da Sie, wie Sie sagten, nicht viel mit dem Produkt herumspielen können. Bei jeder Neugestaltung eines Autos werden jedoch die Erfahrungen aus früheren Bauten herangezogen, und hinter jedem Gebäude stecken rund tausend Jahre Fundamentaldaten über die Bedeutung von Raum, Licht, Isolierung, Festigkeit usw. Sowohl in den Gebäuden als auch in den Details werden Änderungen vorgenommen und Verbesserungen vorgenommen und in Redesigns für neuere.

Auch Teile werden getestet. Möglicherweise nicht genau im selben Stil wie Software, aber mechanische Teile (Räder, Zünder, Kabel) werden normalerweise gemessen und belastet, um sicherzustellen, dass die Größen korrekt sind, keine Abnormalitäten erkennbar sind usw. Gemessen tippen sie auf Steine, um defekte zu erkennen, sie werden möglicherweise tatsächlich in der einen oder anderen Konfiguration getestet, oder sie zeichnen eine begrenzte Darstellung einer großen Gruppe, um sie wirklich zu testen.

All das können Sie mit TDD umsetzen.

Und in der Tat ist das Testen keine Garantie. Programme stürzen ab, Autos fahren kaputt und Gebäude fangen an, lustige Dinge zu tun, wenn der Wind weht. Aber ... "Sicherheit" ist keine boolesche Frage. Selbst wenn Sie nicht immer alles einbeziehen können, ist es besser, 99% der Eventualitäten abzudecken, als nur 50%. Nicht testen und dann herausfinden, dass sich der Stahl nicht gut gesetzt hat und brüchig ist und beim ersten Hammerschlag bricht, wenn Sie gerade Ihre Hauptstruktur aufbauen, ist eine reine Geldverschwendung. Dass es noch andere Bedenken gibt, die dem Gebäude schaden könnten, macht es nicht weniger dumm, wenn ein leicht vermeidbarer Fehler Ihr Design zum Erliegen bringt.

In Bezug auf die TDD-Praxis ist dies eine Frage des Ausgleichs. Die Kosten für die einfache Ausführung (z. B. nicht testen und dann die Teile später abholen) im Vergleich zu den Kosten für die andere Ausführung. Es ist immer ein Gleichgewicht. Denken Sie jedoch nicht, dass in anderen Entwurfsprozessen keine Tests und TDD vorhanden sind.


7
+1, wenn darüber gesprochen wird, wo Tests in der Fertigung stattfinden. Großartiger Punkt.
Adam Lear

11
Sie sagen "Teile sind geprüft". Klar, aber nicht testgetrieben ausgelegt. Ein Flugzeugteil wird nicht testgetrieben entworfen, sondern architektonisch im Voraus. Die Ähnlichkeiten mit TDD sind hier nicht vorhanden.
CesarGon

3
Hinzu kommt: Bei TDD geht es meiner Meinung nach hauptsächlich darum, wie Sie sicherstellen können, dass Sie Teile überprüfen können, anstatt am Ende ein großes "Alles oder Nichts". Aber das Adagium von TDD, "erst einen Test erstellen", ist nicht "erst einen Test durchführen, bevor Sie überlegen, was Sie erreichen möchten". Weil das Denken an einen Test Teil des Entwerfens ist. Das Festlegen, was genau das Teil tun soll, ist das Entwerfen. Bevor Sie anfangen zu tippen, haben Sie bereits einige Entwürfe gemacht. (Auf diese Weise, denke ich, impliziert der Begriff 'testgetriebenes Design' irreführend einen Einbahnstraßenpfad, bei dem es sich tatsächlich um eine Rückkopplungsschleife handelt.)
Inca

2
+1: Software ist reines Design. Die Brückenanalogie in dieser Frage ist völlig falsch. TDD wendet ausschließlich externe Komponententests an. Test-Driven Design gilt für alle Designebenen.
S.Lott

3
@CesarGon: Nein, TDD treibt die ENTWICKLUNG durch Tests voran . Das ist etwas anderes, als das Design voranzutreiben. Das Design bestimmt, wie Sie das System verwenden und welche Tests Sie implementieren müssen, um dieses Verhalten zu replizieren. Die Implementierung dieser Tests hilft Ihnen jedoch häufig dabei , das Design zu verfeinern .
Deworde

26

IMO, die meisten Erfolgsgeschichten für TDD sind gefälscht und nur für Marketingzwecke gedacht. Es kann sehr wenig Erfolg damit geben, aber nur für kleine Anwendungen. Ich arbeite an einer großen Silverlight-Anwendung, in der TDD-Prinzipien verwendet werden. Die Anwendung hat Hunderte von Tests, ist aber immer noch nicht stabil. Einige Teile der Anwendung können aufgrund der komplexen Benutzerinteraktionen nicht getestet werden. Resultierende Tests mit vielen Verspottungen und schwer zu verstehendem Code.

Als wir TDD ausprobiert haben, scheint alles gut zu sein. Ich konnte viele Tests schreiben und die Teile, die für einen Komponententest schwierig sind, verhöhnen. Sobald Sie eine ausreichende Menge an Code haben und eine Schnittstellenänderung erforderlich ist, sind Sie fertig. Viele Tests müssen repariert werden und Sie werden mehr Tests als die eigentliche Änderung im Code neu schreiben.

Peter Norvig erklärt seine Sicht auf TDD im Coders At Work-Buch.

Seibel: Was ist mit der Idee, Tests zu verwenden, um das Design voranzutreiben?

Norvig: Ich sehe Tests eher als Mittel zur Fehlerkorrektur als als Mittel zur Gestaltung. Diese extreme Herangehensweise zu sagen: "Nun, das erste, was Sie tun, ist, einen Test zu schreiben, der besagt, dass ich am Ende die richtige Antwort bekomme." brauchen als nächstes? “- das scheint mir nicht der richtige Weg zu sein, etwas zu entwerfen. Es scheint, als wäre dies nur dann sinnvoll, wenn es so einfach wäre, dass die Lösung vorherbestimmt wurde. Ich denke, du musst zuerst darüber nachdenken. Sie müssen sagen: „Was sind die Teile? Wie kann ich Tests für Teile schreiben, bis ich weiß, was einige von ihnen sind? “Und wenn Sie das getan haben, ist es eine gute Disziplin, Tests für jedes dieser Teile durchzuführen und zu verstehen, wie sie miteinander interagieren und die Grenzfälle und so weiter. Die sollten alle Tests haben. Aber ich glaube nicht, dass Sie das ganze Design fahren, indem Sie sagen: "Dieser Test ist fehlgeschlagen."


7
Wenn Sie diese Fakten nun den Mitarbeitern und Beratern von TDD mitteilen, erhalten Sie die Antwort:well, you haven't done TDD right!
Navaneeth KN,

10
Und sie hätten recht. Wir machen BDD / TDD auf einem sehr hochvolumigen System und es hat gut funktioniert. Die Tests sollen Ihnen mitteilen, dass Sie das erwartete Verhalten verletzt haben. Wenn Sie dies später ändern und die Tests "brechen", machen Sie es in der Tat falsch. Die Tests sollten zuerst geändert werden, um das NEUE Verhalten des Systems zu bestätigen, dann ändern Sie es. Und ja, wenn Sie es richtig machen, schreiben Sie Ihre Tests beginnend mit "Was muss dieses Ding tun?" Und der Prozess des Schreibens des Tests hilft Ihnen zu überlegen, "Was muss die IT tun, um ihre Arbeit zu erledigen?". Oh, und es wurden nie Berater eingesetzt ...
Andy

4
Das Durchführen vieler Tests entbindet Sie nicht von der Erstellung eines ordnungsgemäßen Designs. Ein stark gekoppeltes Design, unabhängig davon, wie viele Tests darauf aufbauen, ist und bleibt fragil. Das Einbetten von Tests in dieses Design kann das Ganze sogar noch schlimmer machen.
Newtopian

3
Es geht nicht darum, etwas falsch zu machen oder ein hoch gekoppeltes Design zu sein. Tatsache ist, dass sich Schnittstellen ändern. Das bedeutet, dass alle Tests, die diese Schnittstelle verwenden, geändert werden müssen. Bei großen Systemen kann es zu einer Überlastung der Implementierung kommen, wenn die Tests mit den erforderlichen Änderungen synchronisiert werden. Dies wird ein noch größeres Problem, wenn Sie eine agile Entwicklung durchführen, da die Wahrscheinlichkeit von Schnittstellenänderungen sehr viel größer ist. Es ist lustig, wie die Befürworter der Methodik darauf bestehen, dass Sie es falsch machen, wenn Methoden nicht funktionieren. Es ist wahrscheinlicher, dass die Methodik nicht für alle Problembereiche geeignet ist.
Dunk

2
Nach meiner Erfahrung funktioniert TDD für kleine Anwendungen oder Module. Wenn ich an etwas Komplexem arbeiten muss, bremst mich TDD, weil es mich dazu zwingt, eine detaillierte (ausführbare) Spezifikation zu schreiben, bevor ich das Gesamtbild klar im Kopf habe: Ich verliere mich zu früh in Details und muss es oft tun Wirf eine ganze Reihe von Tests weg, wenn ich feststelle, dass ich bestimmte Klassen nicht benötige (ich spiele immer noch mit dem Design). In solchen Fällen ziehe ich es vor, zuerst ein vernünftiges Gesamtdesign zu erstellen und dann die Implementierungsdetails zu präzisieren (möglicherweise mithilfe von TDD).
Giorgio

25

Test Driven Design funktioniert für mich aus folgenden Gründen:

Es ist eine ausführbare Form der Spezifikation.

Dies bedeutet, dass Sie aus den Testfällen sehen können:

  1. DASS der aufgerufene Code die Spezifikation vollständig erfüllt, da die erwarteten Ergebnisse in den Testfällen genau dort sind. Eine Sichtprüfung (die erwartet, dass die Testfälle bestanden werden) kann sofort sagen, "oh, dieser Test prüft, ob der Aufruf von invoiceCompany in dieser Situation zu DIESEM Ergebnis führen sollte".
  2. WIE der Code aufgerufen werden soll. Die für die Tests tatsächlich erforderlichen Schritte werden direkt ohne externes Gerüst angegeben (Datenbanken werden verspottet usw.).

Sie schreiben die Ansicht zuerst von außen.

Häufig wird Code so geschrieben, dass Sie zuerst das Problem lösen und dann überlegen, wie der soeben geschriebene Code aufgerufen werden soll. Dies führt häufig zu einer umständlichen Benutzeroberfläche, da es häufig einfacher ist, "nur ein Flag hinzuzufügen" usw. Wenn Sie sich überlegen, dass "DAS müssen wir tun, damit die Testfälle so aussehen", drehen Sie dies um. Dies ergibt eine bessere Modularität, da der Code gemäß der aufrufenden Schnittstelle geschrieben wird und nicht umgekehrt.

Dies führt in der Regel zu einem saubereren Code, der weniger erklärende Dokumentation erfordert.

Sie werden schneller erledigt

Da Sie die Spezifikation für ausführbare Formulare haben, sind Sie fertig, wenn die vollständige Testsuite bestanden wurde. Sie können weitere Tests hinzufügen, wenn Sie die Dinge detaillierter klären, aber als Grundprinzip haben Sie einen sehr klaren und sichtbaren Indikator für den Fortschritt und wenn Sie fertig sind.

Dies bedeutet, dass Sie erkennen können, wann Arbeit erforderlich ist oder nicht (hilft es, einen Test zu bestehen), und am Ende weniger tun müssen.

Für diejenigen, die darüber nachdenken, ist es möglicherweise hilfreich, TDD für Ihre nächste Bibliotheksroutine zu verwenden. Richten Sie langsam eine ausführbare Spezifikation ein und lassen Sie den Code die Tests bestehen. Anschließend steht die ausführbare Spezifikation allen Benutzern zur Verfügung, die sehen möchten, wie die Bibliothek aufgerufen wird.

Kürzlich durchgeführte Studie

"Die Ergebnisse der Fallstudien zeigen, dass die Fehlerdichte vor der Freigabe der vier Produkte im Vergleich zu ähnlichen Projekten, bei denen die TDD-Praxis nicht angewendet wurde, zwischen 40% und 90% abnahm. Subjektiv verzeichneten die Teams einen Anstieg von 15 bis 35% erste Entwicklungszeit nach Einführung von TDD. " ~ Ergebnisse und Erfahrungen von 4 Industrieteams


5
Ich möchte hinzufügen, dass Sie tatsächlich eine einigermaßen vernünftige und klare Richtlinie haben, wann Sie fertig sind. Ohne ein klares Verfahren, mit dem objektiv überprüft werden kann, ob Sie die vorliegende Aufgabe ausgeführt haben, ist es schwierig, dies zu wissen. Meine eigene Erfahrung beinhaltet viele Stunden und Tage, die damit verschwendet wurden, zu "verhandeln", ob eine Aufgabe erledigt wurde, und ein kontinuierliches, ständiges Bewegen der Linie. Dies wirkt sich auf alle Ebenen des Projektmanagements aus, einschließlich der Zeitplanung. Wie können Sie eine solche Aufgabe planen? Klarere Ziele mit schnellerer Abwicklung erhöhen den Durchsatz UND die Kommunikation.
Edward Strange

Dies sollte die akzeptierte Antwort sein.
Niing

19

Beim Erstellen von Software handelt es sich nicht um das Schreiben des Codes. Kein Softwareprojekt sollte zuerst ohne einen Plan für einen breiten Anwendungsbereich gestartet werden. Genau wie ein Projekt, bei dem zwei Ufer eines Flusses überbrückt werden sollen, braucht man einen solchen Plan zuerst.

Der TDD-Ansatz bezieht sich (meistens) auf Unit-Tests - zumindest neigen die Leute dazu, darüber nachzudenken -, bei denen die einfachsten Teile des Software-Codes erstellt werden. Wenn alle Funktionen und Verhaltensweisen bereits definiert wurden und wir tatsächlich wissen, was wir erreichen möchten.

In der Tragwerksplanung sieht es ungefähr so ​​aus:

'Wir haben diese beiden Metallteile miteinander verbunden, und die Verbindung muss Scherkräfte in der Größenordnung von x aushalten. Lassen Sie uns testen, welche Verbindungsmethode hierfür am besten geeignet ist. '

Um zu testen, ob die Software als Ganzes funktioniert, entwickeln wir andere Arten von Tests wie Usability-Tests, Integrationstests und Abnahmetests. Auch diese sollten definiert werden, bevor die eigentliche Arbeit am Schreiben des Codes beginnt, und werden ausgeführt, nachdem die Komponententests grün sind.

Siehe V-Modell: http://en.wikipedia.org/wiki/V-Model_%28software_development%29

Mal sehen, wie es für eine Brücke funktionieren würde:

  1. Eine lokale Regierung sagt zu einer Brückenbaufirma: "Wir brauchen eine Brücke, um diese beiden Punkte zu verbinden. Die Brücke muss in der Lage sein, n Verkehr pro Stunde zuzulassen und für den 21. Dezember 2012 fertig zu sein" - das ist eine Definition von Akzeptanztest: Das Unternehmen erhält keinen vollen Geldbetrag (oder kein Geld), wenn es diesen Test nicht besteht.

  2. Die Geschäftsführung entscheidet über den Projektablauf. Sie stellen Arbeitsteams auf und setzen Ziele für jedes Team. Wenn die Teams diese Ziele nicht erreichen, wird die Brücke nicht rechtzeitig gebaut. Allerdings gibt es hier ein gewisses Maß an Flexibilität. Wenn eines der Teams Probleme hat, kann das Unternehmen dies ausgleichen, indem es die Anforderungen ändert, Subunternehmer wechselt, mehr Mitarbeiter einstellt usw., sodass das gesamte Projekt weiterhin das unter Punkt 1 festgelegte Ziel erreicht.

  3. In einem Team, das für das Entwerfen bestimmter Brückenkomponenten verantwortlich ist, sieht es so aus, wie im obigen Beispiel. Manchmal liegt die Lösung auf der Hand, weil wir über ein umfangreiches Wissen in Bezug auf das Bauen von Brücken verfügen (es ist, als würde man eine gut getestete Bibliothek in der Softwareentwicklung verwenden - man geht einfach davon aus, dass sie wie angekündigt funktioniert). Manchmal müssen Sie mehrere Designs erstellen und testen, um das beste auszuwählen. Die Kriterien, nach denen die Komponente getestet wird, sind jedoch im Voraus bekannt.


Wenn ich Sie richtig verstehe, sagen Sie, dass TDD in Ordnung ist, solange (a) es nur für Komponententests verwendet wird und (b) es auch von anderen Testmethoden begleitet wird. In diesem Fall kann der Punkt 2 im OP angesprochen werden. Wie würden Sie Punkt Nummer 1 ansprechen?
CesarGon

@CesarGon: TDD eignet sich auch hervorragend für Integrationstests.
Sevenseacat

Punkt 1 läuft darauf hinaus, den Akt nach unten, dass vor der endgültigen Projekt für ein Auto oder eine Brücke akzeptiert wird, geht es durch viele Wiederholungen , bei der alle es um die Details überprüft und getestet gegen die Anforderungen der ‚breiten Anwendungsbereich Plan‘ auferlegt. Dies geschieht hauptsächlich auf Papier (oder im Computerspeicher), da dies in diesem Fall billiger ist. Beachten Sie jedoch, dass häufig physische Prototypen sowohl aus der gesamten Konstruktion (möglicherweise nicht im Bridge-Fall) als auch aus Komponenten erstellt werden.
Mchl

@Karpie: Und auch für Abnahmetests! Sie sollten vorher wissen, was erforderlich ist, damit Ihre Arbeit vom Kunden akzeptiert wird.
Mchl

1
OK dann. Fast das erste Team, das mit der Arbeit beginnt, ist ein Team von Architekten, das eine Brücke entwerfen soll, die die Kriterien des Kunden erfüllt, gleichzeitig aber auch billig ist und möglicherweise gut aussieht und nicht bei der ersten stärkeren Windböe untergeht. Das Team schlägt möglicherweise einige grobe Entwürfe vor, die diesen Kriterien mehr oder weniger entsprechen. Wählen Sie dann einen aus und bearbeiten Sie ihn im Detail. Wiederholen Sie diese, wiederholen Sie sie, bis das Design fertig ist (dh, es erfüllt die angegebenen Kriterien und ist so detailliert, dass weitere Projektphasen können beginnen)
Mchl

18

In meinen Gedanken funktioniert TDD weil

  • Sie müssen festlegen, was die Einheit tun soll, bevor Sie sich für eine Implementierung mit einer Genauigkeit entscheiden, die in der Regel nicht in Spezifikationen oder Anforderungsdokumenten aufgeführt ist
  • Es macht Ihren Code von Natur aus wiederverwendbar, da Sie ihn sowohl in Tests als auch in Produktionsszenarien verwenden müssen
  • Es empfiehlt sich, Code in kleinerem Format zu schreiben, um Chunks zu testen, was tendenziell zu besseren Designs führt

Speziell auf die Punkte, die Sie erhöhen

  • Code ist formbarer als Ziegel oder Stahl, daher ist es billiger, ihn zu modifizieren. Es ist immer noch billiger, wenn Sie Tests durchführen, um sicherzustellen, dass sich das Verhalten nicht ändert
  • TDD ist keine Entschuldigung dafür, kein Design zu machen - eine Architektur auf hohem Niveau wird generell immer noch empfohlen, nur nicht mit zu vielen Details. Von Big Up Front Design wird abgeraten, aber es wird empfohlen, gerade genug zu entwerfen
  • TDD kann nicht garantieren, dass ein System funktioniert, verhindert jedoch, dass viele kleine Fehler übersehen werden, die sonst übersehen würden. Auch weil es im Allgemeinen besser faktorisierten Code fördert, ist es oft einfacher zu verstehen, so dass es weniger wahrscheinlich ist, dass es fehlerhaft ist

3
Sie sollten auch hinzufügen, dass Sie beim Erkennen von Fehlern sicherstellen können, dass diese nicht wiederholt werden, wenn Sie einen weiteren Test hinzufügen.
Andy

16

TL; DR

Programmieren ist immer noch eine Designaktivität, es ist keine Konstruktion. Durch das Schreiben von Unit-Tests wird nur bestätigt, dass der Code das tut, was er tut, und nicht, dass er etwas Nützliches tut. Testfehler sind der wahre Wert, da Sie Fehler frühzeitig erkennen können.

Code ist Design

In Kapitel 7 von PPP spricht "Onkel Bob" direkt über dieses Problem. Sehr früh in diesem Kapitel verweist er auf einen ausgezeichneten Artikel von Jack Reeves, in dem er vorschlägt, dass Code Design ist (der Link führt zu einer Seite, auf der alle drei Artikel zu diesem Thema zusammengefasst sind).

Das Interessante an diesem Argument ist, dass er darauf hinweist, dass die Erstellung von Software im Gegensatz zu anderen Ingenieurdisziplinen, in denen das Bauen eine sehr teure Aktivität ist, relativ kostenlos ist (Hit Compile in Ihrer IDE und Sie haben Ihre erstellte Software). Wenn Sie das Schreiben von Code als Entwurfsaktivität statt als Konstruktionsaktivität betrachten, ist der Rot-Grün-Refaktor-Zyklus im Grunde eine Übung im Entwurf. Ihr Design entwickelt sich, wenn Sie Tests schreiben, den Code, um diese zu erfüllen, und den neuen Code umzugestalten, um ihn in das vorhandene System zu integrieren.

TDD als Spezifikation

Die Unit-Tests, die Sie für TDD schreiben, sind eine direkte Übersetzung der Spezifikation, so wie Sie sie verstehen. Wenn Sie Code schreiben, der Ihre Spezifikation nur minimal erfüllt (Ihre Tests werden grün), ist der gesamte von Ihnen geschriebene Code für einen bestimmten Zweck da. Ob dieser Zweck erfüllt wurde oder nicht, wird durch einen wiederholbaren Test bestätigt.

Schreiben Sie Tests zur Funktionalität

Ein häufiger Fehler beim Komponententest tritt auf, wenn Sie die Tests nach dem Code schreiben und am Ende testen, ob der Code das tut, was er tut. Mit anderen Worten, Sie werden Tests wie diese sehen

public class PersonTest:Test
{
   [Test]
   TestNameProperty()
   {
      var person=new Person();
      person.Name="John Doe";
      Assert.AreEqual("John Doe", person.Name);
   }
}

Obwohl ich denke, dass dieser Code nützlich sein könnte (stellen Sie sicher, dass jemand mit einer einfachen Eigenschaft nichts Obszönes angestellt hat). Es dient nicht zur Validierung einer Spezifikation. Und wie Sie sagten, bringt Sie das Schreiben solcher Tests nur so weit.

Während Grün gut ist, liegt der Wert in Rot. Ich hatte meinen ersten wahren "Aha" -Moment in TDD, als ich einen unerwarteten Testfehler bekam. Ich hatte eine Reihe von Tests, die ich für ein Framework hatte, das ich baute. Ich habe eine neue Funktion hinzugefügt und einen Test dafür geschrieben. Dann habe ich den Code geschrieben, um den Test zu bestehen. Kompilieren, testen ... hat beim neuen Test ein Grün bekommen. Aber auch bei einem anderen Test, von dem ich nicht erwartet hatte, dass er rot wird, wurde er rot.

Wenn ich mir das Scheitern anschaue, atme ich erleichtert auf, weil ich bezweifle, dass ich diesen Fehler längere Zeit hätte finden können, wenn ich diesen Test nicht durchgeführt hätte. Und es war ein sehr böser Fehler zu haben. Glücklicherweise hatte ich den Test und er sagte mir genau, was ich tun musste, um den Fehler zu beheben. Ohne den Test hätte ich mein System weiter aufgebaut (mit dem Fehler, der andere Module infiziert, die von diesem Code abhängen), und bis der Fehler entdeckt wurde, wäre es eine Hauptaufgabe gewesen, ihn richtig zu beheben.

Der wahre Vorteil von TDD besteht darin, dass wir Änderungen mit rücksichtsloser Zurückhaltung vornehmen können. Es ist wie ein Sicherheitsnetz für die Programmierung. Überlegen Sie, was passieren würde, wenn ein Trapezkünstler einen Fehler macht und fällt. Mit dem Netz ist es ein peinlicher Fehler. Ohne ist es eine Tragödie. In gleicher Hinsicht erspart Ihnen TDD, Fehler auf den Knochen in Katastrophen umzuwandeln, die zum Tod von Projekten führen.


4
Der Wert von roten Tests, die Fehler aufspüren, ist ein Attribut von Unit Testing im Allgemeinen und nicht von TDD im Besonderen.
Robert Harvey

2
Da hast du recht. Aber die Wahrscheinlichkeit, dass ich diesen spezifischen Fehler bei Post-Hoc-Unit-Tests hätte finden können, ist geringer.
Michael Brown

1
Können Sie diese Behauptung mit Beweisen, Daten oder soliden Analysen belegen?
CesarGon

1
@CesarGon In dieser Studie wird von Programmierern, die an einem kleinen Projekt arbeiten, vorgeschlagen, dass Entwickler, die TDD verwenden, Code mit einer besseren Testabdeckung erstellen als diejenigen, die nachträglich testen (92% -98% gegenüber 80% -90%) und folglich mehr abfangen Fehler während der Entwicklung (18% weniger Fehler im mit TDD erstellten Code).
Jules

11

Sie werden niemanden finden, der Test Driven Development oder Test Driven Design befürwortet (sie sind anders), das besagt, dass Tests Anwendungen beweisen. Nennen wir das also einfach einen Strohmann und fertig.

Sie werden niemanden finden, der TDD nicht mag oder von ihm nicht beeindruckt ist. Tests sind eine Verschwendung von Zeit und Mühe. Tests beweisen zwar keine Anwendungen, sind jedoch bei der Fehlersuche sehr hilfreich.

Mit diesen beiden Worten unternimmt keine der beiden Seiten etwas anderes, um tatsächlich Tests an der Software durchzuführen. Beide testen. Beide verlassen sich auf Tests, um so viele Fehler wie möglich zu finden, und beide verwenden Tests, um zu überprüfen, ob ein Softwareprogramm funktioniert und zu dem Zeitpunkt entdeckt werden kann. Niemand mit einer halben Ahnung verkauft Software ohne zu testen und niemand mit einer halben Ahnung erwartet, dass das Testen den Code, den sie verkaufen, völlig fehlerfrei macht.

Der Unterschied zwischen TDD und Nicht-TDD besteht also nicht darin, dass Tests durchgeführt werden. Der Unterschied besteht darin, wann die Tests geschrieben werden. In TDD werden Tests VOR der Software geschrieben. Bei Nicht-TDD-Tests erfolgt die Erstellung nach oder in Abstimmung mit der Software.

Das Problem, das ich in Bezug auf Letzteres gesehen habe, ist, dass das Testen darauf abzielt, dass die Software mehr als das gewünschte Ergebnis oder die gewünschte Spezifikation geschrieben wird. Selbst wenn das Testteam vom Entwicklungsteam getrennt ist, tendiert das Testteam dazu, sich die Software anzusehen, damit zu spielen und Tests zu schreiben, die darauf abzielen.

Eine Sache, die von denjenigen, die den Projekterfolg studieren, immer wieder bemerkt wurde, ist, wie oft ein Kunde das darlegt, was er will, die Entwickler davonlaufen und etwas schreiben und wenn sie zu dem Kunden zurückkehren, sagen sie "erledigt". Es stellt sich heraus, dass es absolut NICHT das ist, wonach der Kunde gefragt hat. "Aber es besteht alle Tests ..."

Das Ziel von TDD ist es, dieses "Zirkelargument" zu durchbrechen und die Grundlage für die Tests zu schaffen, mit denen die Software getestet wird, die nicht die Software selbst ist. Die Tests sind so geschrieben, dass sie auf das vom "Kunden" gewünschte Verhalten abzielen. Die Software wird dann geschrieben, um diese Tests zu bestehen.

TDD ist Teil der Lösung, die dieses Problem lösen soll. Es ist nicht der einzige Schritt, den Sie machen. Sie müssen außerdem sicherstellen, dass mehr und häufiger Kundenfeedback eingeht.

Nach meiner Erfahrung ist es jedoch sehr schwierig, TDD erfolgreich umzusetzen. Es ist schwierig, Tests zu schreiben, bevor es ein Produkt gibt, da für viele automatisierte Tests etwas zum Spielen erforderlich ist, damit die Automatisierungssoftware richtig funktioniert. Es ist auch schwierig, Entwickler, die nicht an Unit-Tests gewöhnt sind, dazu zu bewegen. Immer wieder habe ich die Leute in meinem Team angewiesen, die Tests ZUERST zu schreiben. Ich habe noch nie einen dazu gebracht. Letztendlich haben Zeitdruck und Politik alle Anstrengungen zunichte gemacht, so dass wir nicht einmal mehr Unit-Tests durchführen. Dies führte natürlich unweigerlich dazu, dass das Design versehentlich und streng gekoppelt wurde, so dass es jetzt unerschwinglich und kostspielig wäre, es zu implementieren, selbst wenn wir es wollten. Dies zu vermeiden, ist es, was TDD letztendlich für Entwickler bereitstellt.


+1 Danke für die umfassende Antwort, Noah. Ich stimme zu, dass der Hauptunterschied zwischen TDD und Nicht-TDD darin besteht, dass die Tests geschrieben werden. Ich denke jedoch auch, dass das erste "D" in TDD für "gesteuert" steht, was bedeutet, dass in TDD die gesamte Entwicklung von den Tests gesteuert wird. Das finde ich am rätselhaftesten. Ich habe keine Probleme damit, Tests zu schreiben, bevor ich tatsächlich konstruiere, was getestet werden soll. Aber Tests fahren lassen? Wie unterscheidet sich das von einem grünen Licht, etwas zu tun, solange das oberflächliche (dh das Ergebnis) in Ordnung ist?
CesarGon

Nun, Cesar, was würden Sie als besseres, objektives Kriterium für die Entscheidung, wann eine Entwicklungsaufgabe abgeschlossen ist, vorschlagen? Wenn der Test, wie in TDD, die Spezifikation ist, auf die ein Entwickler abzielt, hat der Entwickler seine Arbeit erledigt, als der Test bestanden wurde, nein? Ja, es kann Fehler im Test geben, genauso wie es Fehler in jeder Spezifikation geben kann. Das ist jedoch nicht die Aufgabe der Entwickler. Wenn der Test fehlerhaft ist, wird er repariert, die Entwicklung zielt auf das neue Ziel ab und wenn alles grün ist, sind sie fertig. Es funktioniert, weil es IMMER einen Test gibt, der bestanden werden muss ... keine zusätzlichen, undokumentierten Flusen.
Edward Strange

3
Vielleicht habe ich mich nicht klar ausgedrückt. Tests können eine gute Möglichkeit sein, um festzustellen, wann Sie fertig sind. Aber ich denke nicht, dass sie ein guter Weg sind, um zu entscheiden, was Sie bauen müssen. Und bei TDD stelle ich fest, dass die Leute Tests verwenden, um zu entscheiden, was sie bauen sollen. Ist das auch deine Erfahrung?
CesarGon

Nein, unsere Builds sind automatisiert. Sie werden durch Veränderungen ausgelöst. Wie gesagt, TDD ist nur ein Teil der Lösung.
Edward Strange

9

Design zuerst

TDD ist keine Entschuldigung, um Design zu überspringen. Ich habe viele Sprünge im "agilen" Zug gesehen, weil sie mit dem Codieren sofort beginnen konnten. Echte Agilität bringt Sie viel schneller zur statistischen Codierung als die bewährten Methoden der (anderen Fach-) Technik, die den Wasserfallprozess inspiriert haben.

Aber früh testen

Wenn man sagt, dass die Tests das Design vorantreiben, bedeutet dies einfach, dass man Tests sehr früh in der Designphase anwenden kann, lange bevor sie abgeschlossen sind. Wenn Sie diese Tests durchführen, wird Ihr Design stark beeinflusst, indem Sie die Grauzonen herausfordern und gegen die reale Welt antreten, lange bevor das Produkt fertiggestellt ist. Sie müssen häufig zum Design zurückkehren und es anpassen, um dies zu berücksichtigen.

Test und Design ... ein und dasselbe

Meiner Meinung nach ist es bei TDD einfach so, dass der Test ein wesentlicher Bestandteil des Designs ist, anstatt dass am Ende etwas getan wird, um ihn zu validieren. Wenn Sie TDD immer häufiger einsetzen, werden Sie sich immer mehr darüber im Klaren, wie Sie Ihr System beim Entwerfen zerstören oder zerstören können. Persönlich mache ich meine Tests nicht immer zuerst. Sicher, ich mache die offensichtlichen (Einheiten-) Tests an einer Schnittstelle, aber der eigentliche Gewinn ergibt sich aus den Integrations- und Spezifikationstests, die ich erstelle, wenn ich mir überlege, wie dieses Design auf neue und kreative Weise funktionieren kann. Sobald mir eine Möglichkeit einfällt, programmiere ich einen Test und sehe, was passiert. Manchmal kann ich mit der Konsequenz leben, in diesem Fall verschiebe ich den Test in ein separates Projekt, das nicht Teil des Haupt-Builds ist (da es weiterhin fehlschlagen wird).

Wer fährt dann die Show?

In TDD bedeutet das hier Fahren einfach, dass Ihre Tests Ihr Design so stark beeinflussen, dass man das Gefühl hat, dass sie es tatsächlich fahren. Wie auch immer man damit aufhört und hier verstehe ich Ihre Bedenken, es ist ein bisschen beängstigend ... wer treibt die Show an?

SIE fahren, nicht die Tests. Die Tests sind da, damit Sie auf Ihrem Weg ein gutes Maß an Vertrauen in das, was Sie geschaffen haben, gewinnen und so besser wissen können, dass es auf einem soliden Grund ruht.

fest, solange die Tests fest sind

Genau , daher der im TDD gefahrene. Es ist nicht so sehr, dass die Tests das Ganze vorantreiben, aber sie werden einen so tiefen Einfluss darauf haben, wie Sie Dinge tun, wie Sie Ihr System entwerfen und denken, dass Sie einen großen Teil Ihres Denkprozesses an Tests delegieren und im Gegenzug Sie werden einen tiefen Einfluss auf Ihr Design haben.

ja aber wenn ich das mit meiner brücke mache th ....

genau dort aufhören ... Software-Engineering unterscheidet sich SEHR von allen anderen Engineering-Praktiken auf dem Markt. Tatsächlich hat Software-Engineering viel mehr mit Literatur zu tun. Man kann ein fertiges Buch nehmen, 4 Kapitel daraus rippen und zwei neue Kapitel schreiben, um sie zu ersetzen, und sie wieder in das Buch stecken, und man hat immer noch ein gutes Buch. Mit guten Tests und Software können Sie jeden Teil Ihres Systems rippen und durch einen anderen ersetzen, und die Kosten dafür sind nicht viel höher als die, die es ursprünglich erstellt hat. Wenn Sie Ihre Tests durchgeführt haben und es ihnen erlaubt haben, Ihr Design ausreichend zu beeinflussen, kann dies sehr wohl billiger sein als die erstmalige Erstellung, da Sie sicher sind, dass dieser Ersatz nicht die Anforderungen der Tests erfüllt.

Wenn es sooo gut ist, warum funktioniert es nicht immer?

Weil das Testen eine SEHR andere Denkweise erfordert als das Bauen. Nicht jeder ist in der Lage, zurückzuschalten, und tatsächlich werden einige Menschen nicht in der Lage sein, geeignete Tests zu erstellen, nur weil sie sich nicht dazu entschließen können, ihre Schöpfung zu zerstören. Dies führt zu Projekten mit zu wenigen Tests oder Tests, die gerade ausreichen, um eine Zielmetrik zu erreichen (man denke an die Codeabdeckung). Sie werden gerne Pfad- und Ausnahmetests durchführen, aber die Eckfälle und Randbedingungen vergessen.

Andere werden sich nur auf Tests verlassen, die teilweise oder vollständig auf das Design verzichten. Jedes Mitglied tut seine Sache und integriert sich dann miteinander. Design ist zuallererst ein Kommunikationsinstrument. Wir setzen alles daran, um zu sagen, dass ich hier sein werde. Skizzen, die besagen, dass hier die Türen und Fenster sein werden. Ohne dies ist Ihre Software zum Scheitern verurteilt, unabhängig davon, wie viele Tests Sie in das Ding stecken. Integration und Zusammenführung werden immer schmerzhaft sein und es wird ihnen an Tests auf höchstem Abstraktionsniveau mangeln.

Für diese Teams ist TDD möglicherweise nicht der richtige Weg.


7

Mit TDD schreiben Sie in der Regel keinen Code, der nicht einfach oder schnell zu testen ist. Dies mag klein erscheinen, kann sich jedoch tiefgreifend auf ein Projekt auswirken, da es sich darauf auswirkt, wie einfach es ist, Fehler mit Tests umzugestalten, zu testen, zu reproduzieren und Korrekturen zu überprüfen.

Es ist für einen neuen Entwickler im Projekt auch einfacher, auf dem Laufenden zu bleiben, wenn Sie Code mit besseren Faktoren haben, der durch Tests unterstützt wird.


2
Ich mag das - es unterstreicht den Punkt, dass es nicht so sehr TDD, das die Vorteile schafft (obwohl Unit-Tests eindeutig einen enormen Wert haben), als die Art von Code ist, den es in dem Sinne erzeugt, dass es testbar ist (isoliert) und Daraus ergeben sich alle möglichen guten Dinge (Trennung von Bedenken, IoC und Abhängigkeitsinjektion usw. usw.)
Murph

1
@ Murph yeah TDD hilft Ihnen, ehrlich zu bleiben :)
Alb

1
Um ehrlich zu sein, bin ich nicht wirklich von dem Argument überzeugt, dass es "einfacher ist, sich auf den neuesten Stand zu bringen". Die Tests mögen hilfreich sein, aber der Code (insgesamt, nicht unbedingt isoliert) kann etwas schwieriger zu dekodieren sein, da einige Dinge dies tun Erscheint wie von Zauberhand, dh Sie wissen nicht, welche Implementierung von IInjectedThing Sie verwenden.
Murph

@Murph Die Theorie ist , dass die Umsetzung IInjectedThing auch gut und durch gute Tests abgedeckt ausgelegt ist, so dass Sie nicht wirklich brauchen , zu wissen , was es in der Lage sein wird , eine Klasse zu verstehen , dass es in injiziert wird .
Adam Lear

@Anna - ja, bis zu einem gewissen Punkt ... wenn Sie herausfinden möchten, wo etwas kaputt ist (ich bin immer der Meinung, dass die Suche nach Fehlern eine gute Möglichkeit ist, in einem Projekt Fuß zu fassen) oder wo etwas geändert werden muss / hinzugefügt, müssen Sie wissen, wo. Auch wenn das gut gekapselt ist, müssen Sie es noch finden ... und wenn es bedeutet, etwas zu ersetzen (neue Implementierung von IWhatsit), müssen Sie wissen, wie Sie die alternative Implementierung verwenden. Ich bezweifle auch hier nicht, dass die Konstruktion schlecht ist - zu viele Beweise für das Gegenteil -, sondern lege vielmehr nahe, dass einige Dinge möglicherweise weniger offensichtlich sind.
Murph

5

Ich habe viel darüber nachgedacht, obwohl ich selbst nicht so viel TDD praktiziere. Es scheint eine (starke?) Positive Korrelation zwischen der Codequalität und der folgenden TDD zu geben.

1) Meine erste Annahme ist, dass dies (in erster Linie) nicht darauf zurückzuführen ist, dass TDD dem Code (als solchem) "bessere Qualität" hinzufügt. TDD hilft eher dabei, die schlimmsten Teile und Gewohnheiten auszumerzen und somit indirekt die Qualität zu erhöhen.

Ich würde sogar befürworten, dass es nicht der Test selbst ist - es ist der Prozess , diese Tests zu schreiben . Es ist schwierig, Tests für einen fehlerhaften Code zu schreiben und umgekehrt. Und wenn Sie dies beim Programmieren im Hinterkopf behalten, werden viele fehlerhafte Codes eliminiert.

2) Eine andere Sichtweise (dies wird philosophisch) folgt den mentalen Gewohnheiten des Meisters. Man lernt nicht, ein Meister zu werden, indem man seinen "äußeren Gewohnheiten" folgt (ein langer Bart ist gut), man muss seine inneren Denkweisen lernen, und das ist schwer. Und irgendwie können (Anfänger-) Programmierer TDD folgen und ihre Denkweise näher an die des Meisters anpassen.


+1 Ich glaube, du hast es geschafft, Maglob. Mir gefällt besonders Ihre Erklärung, dass "TDD hilft, die schlimmsten Teile und Gewohnheiten auszumerzen und [...] indirekt die Qualität zu erhöhen". Und die Analogie mit dem langen Bart ist auch sehr gut.
CesarGon

Sie schreiben keine Tests für einen fehlerhaften Code, aber Sie schreiben einen Test und schreiben dann den Code, um den Test zu bestehen.

Maglob, für die Liebe zur praktischen Seite der Dinge, haben Sie es am besten abgedeckt. @ Thorbjørn, ich denke, dass Maglob eher dahinter folgte, dass Ihre Tests, wenn Ihr geplantes Design scheitert, mit Sicherheit direkt an dem Grad der Saugkraft aufsaugen müssen, den Sie zu erreichen versuchen, und der faulige Geruch davon sollte in Ihren Tests vorher stinken Sie können sogar jeden tatsächlichen Code schreiben.
Filip Dupanović

3

Der Ansatz "Write Test + Refactor Till Pass" sieht unglaublich anti-engineering aus.

Sie scheinen ein Missverständnis über Refactoring und TDD zu haben.

Beim Code-Refactoring wird der Quellcode eines Computerprogramms geändert, ohne dass das externe Funktionsverhalten geändert wird, um einige der nicht funktionierenden Attribute der Software zu verbessern.

Daher können Sie Code erst dann umgestalten , wenn er erfolgreich ist.

Und bei TDD, insbesondere beim Unit-Testen (was ich als Kernverbesserung betrachte, da andere Tests für mich eher plausibel erscheinen), geht es nicht darum, eine Komponente neu zu entwerfen, bis sie funktioniert. Es geht darum, eine Komponente zu entwerfen und an der Implementierung zu arbeiten, bis die Komponente wie geplant funktioniert.

Es ist auch wichtig zu verstehen, dass es beim Unit- Testen um das Testen von Units geht . Aufgrund der Tendenz, immer viele Dinge von Grund auf neu zu schreiben, ist es wichtig, solche Einheiten zu testen. Ein Bauingenieur kennt bereits die Spezifikationen der von ihm verwendeten Einheiten (die verschiedenen Materialien) und kann erwarten, dass sie funktionieren. Dies sind zwei Dinge, die für Software-Ingenieure häufig nicht zutreffen, und es ist sehr pro-technisch, die Einheiten vor der Verwendung zu testen, da dabei getestete, hochwertige Komponenten verwendet werden.
Wenn ein Bauingenieur die Idee hätte, ein neues Fasergewebe für die Herstellung eines Daches zur Abdeckung eines Stadions zu verwenden, würde man von ihm erwarten, dass er es als Einheit testet, dh die erforderlichen Spezifikationen definiert (z. B. Gewicht, Durchlässigkeit, Stabilität usw.) und danach testen und verfeinern, bis es ihnen entspricht.

Deshalb funktioniert TDD. Denn wenn Sie Software aus getesteten Einheiten erstellen, ist die Wahrscheinlichkeit groß, dass sie funktioniert, wenn Sie sie zusammenstecken, und wenn dies nicht der Fall ist, können Sie davon ausgehen, dass das Problem in Ihrem Klebercode enthalten ist, vorausgesetzt, dass Ihre Tests eine gute Abdeckung aufweisen.

Bearbeiten:
Refactoring bedeutet: keine Änderung der Funktionalität. Beim Unit-Test muss sichergestellt werden, dass das Refactoring den Code nicht beschädigt. TDD soll also sicherstellen, dass Refactoring keine Nebenwirkungen hat.
Die Granularität ist kein Gegenstand der Perspektive, denn wie gesagt, Unit-Tests sind Testeinheiten und keine Systeme, bei denen die Granularität genau definiert ist.

TDD fördert gute Architektur. Es erfordert, dass Sie Spezifikationen für alle Ihre Einheiten definieren und implementieren, sodass Sie gezwungen sind, diese vor der Implementierung zu entwerfen, was ganz im Gegenteil zu Ihrer Vermutung ist. TDD schreibt die Erstellung von Einheiten vor, die einzeln getestet und somit vollständig entkoppelt werden können.
TDD bedeutet nicht, dass ich einen Softwaretest mit Spaghetti-Code mache und die Nudeln rühre, bis sie verstrichen sind.

Im Gegensatz zum Tiefbau entwickelt sich im Software-Engineering ein Projekt in der Regel ständig weiter. Im Tiefbau müssen Sie in Position A eine Brücke bauen, die x Tonnen tragen kann und breit genug für n Fahrzeuge pro Stunde ist.
In der Softwareentwicklung kann der Kunde grundsätzlich zu jedem Zeitpunkt (möglicherweise nach Fertigstellung) entscheiden, ob er eine Brücke mit Doppeldeck wünscht und ob er möchte, dass diese mit der nächsten Autobahn verbunden wird, und dass er möchte, dass sie eine Hebebrücke ist, weil sein Unternehmen vor kurzem begann Segelschiffe zu verwenden.
Software-Ingenieure haben die Aufgabe , das Design zu ändern. Nicht weil ihre Entwürfe fehlerhaft sind, sondern weil das der Modus operandi ist. Wenn die Software ausgereift ist, kann sie auf hoher Ebene neu gestaltet werden, ohne dass alle Komponenten auf niedriger Ebene neu geschrieben werden müssen.

Bei TDD geht es darum, Software mit individuell getesteten, stark entkoppelten Komponenten zu erstellen. Gut ausgeführt, wird es Ihnen helfen, schneller und sicherer auf geänderte Anforderungen zu reagieren als ohne.

TDD stellt zusätzliche Anforderungen an den Entwicklungsprozess, verbietet jedoch keine anderen Methoden zur Qualitätssicherung. Zugegeben, TDD bietet nicht die gleiche Sicherheit wie die formale Verifizierung. Andererseits ist die formale Verifizierung äußerst kostenintensiv und auf Systemebene nicht anwendbar. Und trotzdem könnten Sie beide kombinieren, wenn Sie wollten.

TDD umfasst auch andere Tests als Komponententests, die auf Systemebene durchgeführt werden. Ich finde diese einfach zu erklären, aber schwierig auszuführen und schwer zu messen. Auch sind sie durchaus plausibel. Obwohl ich ihre Notwendigkeit absolut sehe, schätze ich sie nicht wirklich als Ideen.

Am Ende löst kein Tool ein Problem. Werkzeuge erleichtern nur das Lösen eines Problems. Sie können sich fragen: Wie hilft mir ein Meißel bei großartiger Architektur? Wenn Sie gerade Wände bauen möchten, sind gerade Ziegel hilfreich. Und ja, selbstverständlich, wenn Sie einem Idioten dieses Werkzeug geben, wird er es wahrscheinlich irgendwann durch den Fuß schlagen, aber das ist nicht die Schuld des Meißels, so sehr es auch kein Fehler von TDD ist, dass es Anfängern falsche Sicherheit gibt. die schreiben keine guten tests.
Unterm Strich kann man also sagen, dass TDD viel besser funktioniert als kein TDD.


Ich glaube nicht, dass ich ein Missverständnis habe. Ich stimme der Definition von Code-Refactoring zu, die Sie veröffentlicht haben, aber ich denke auch, dass Sie die Granularität der Änderungen im Code überprüfen müssen. Wenn Sie "den Prozess der Änderung des Quellcodes eines Computerprogramms " sagen , müssen Sie erkennen, dass sich aus der Perspektive eines bestimmten Ganzen das Verhalten nicht ändert, aber das Verhalten der Teile ändert sich tatsächlich. So wird Veränderung bewirkt. Außerdem höre ich, warum TDD funktioniert (und ich teile es), aber wie wird die Architektur gemäß meinem ursprünglichen Beitrag behandelt?
CesarGon

@CesarGon: Beitrag aktualisiert.
back2dos

2

Ich mag es nicht, wenn Sie sagen: "Der Test bestimmt die Anforderungen, nicht der Benutzer." Ich denke, Sie erwägen nur Unit-Tests in TDD, während es auch Integrationstests abdeckt.

Schreiben Sie neben dem Testen der Bibliotheken, die die Basis der Software bilden, auch die Tests, die die Interaktionen Ihrer Benutzer mit der Software / Website / was auch immer abdecken. Diese kommen direkt von den Benutzern, und Bibliotheken wie cucumber (http://cukes.info) können Ihre Benutzer sogar die Tests selbst in natürlicher Sprache schreiben lassen.

TDD fördert auch die Flexibilität im Code - wenn Sie die Architektur von etwas für immer entwerfen, wird es unglaublich schwierig sein, diese Änderungen später vorzunehmen, falls erforderlich. Beginnen Sie mit dem Schreiben einiger Tests und schreiben Sie dann einen kleinen Code, der diese Tests besteht. Fügen Sie mehr Tests hinzu, fügen Sie mehr Code hinzu. Wenn Sie den Code radikal ändern müssen, bleiben Ihre Tests bestehen.

Und im Gegensatz zu Brücken und Autos kann eine einzelne Software im Laufe ihrer Lebensdauer enorme Änderungen erfahren, und komplexe Umbauten ohne vorherige Testerstellung sind nur eine Frage der Mühe.


Ich höre Ihnen von den Vorteilen, die Sie für TDD beanspruchen. Soweit ich weiß, gehen Sie jedoch nicht auf die Fragen der Architektur und der Testqualität ein, die ich ausdrücklich in meiner Frage gestellt habe.
CesarGon

@CesarGon: Ich denke, Ihre spezifischen Fragen gelten für jede Art von Test, nicht nur für TDD. Also habe ich mich nur auf die spezifischen Funktionen von TDD konzentriert, die funktionieren.
Sevenseacat

1
Integrationstests sind definitiv sinnvoller als eigenständige Unit-Tests. Die meisten Fehlerfälle, auf die ich stoße, wären bei Unit-Tests nie entdeckt worden, nur wenn das gesamte reale System mit all seinen Blitzen und Pfeifen getestet worden wäre .

2

Ich denke, Sie nähern sich dem ersten Punkt aus dem falschen Winkel.

Aus theoretischer Sicht beweisen wir, dass etwas funktioniert, indem wir es mit Fehlerpunkten vergleichen. Das ist die angewandte Methode. Es gibt viele andere Möglichkeiten, um zu beweisen, dass etwas funktioniert, aber TDD hat sich aufgrund der Einfachheit seines bitweisen Ansatzes etabliert: Wenn es nicht kaputt geht, funktioniert es.

In der Praxis bedeutet dies ausgesprochen: Wir können jetzt mit dem nächsten Schritt fortfahren (nachdem wir TDD erfolgreich angewendet haben, um alle Prädikate zu erfüllen). Wenn Sie sich aus dieser Perspektive TDD nähern, dann geht es nicht darum, Tests + Refactor bis zum Bestehen zu schreiben, sondern darum , dies abgeschlossen zu haben. Ich konzentriere mich jetzt voll und ganz auf das nächste Feature als das Wichtigste .

Überlegen Sie, wie dies für den Tiefbau gilt. Wir bauen ein Stadion, in dem 150000 Zuschauer Platz haben. Nachdem wir bewiesen haben, dass die strukturelle Integrität des Stadions einwandfrei ist, haben wir zuerst die Sicherheit befriedigt . Wir können uns jetzt auf andere Themen konzentrieren, die sofort wichtig werden, wie z. B. Toiletten, Essensstände, Sitzgelegenheiten usw., um die Erfahrung des Publikums angenehmer zu gestalten. Dies ist eine übermäßige Vereinfachung, da TDD noch viel mehr zu bieten hat. Der springende Punkt ist jedoch, dass Sie nicht das bestmögliche Benutzererlebnis erzielen, wenn Sie sich auf neue und aufregende Funktionen konzentrieren und gleichzeitig die Integrität wahren. Sie bekommen es in beiden Fällen zur Hälfte. Ich meine, wie können Sie genau wissen, wieviele Toiletten und wo sollten Sie für 150000 Menschen platzieren? Ich habe in meinem eigenen Leben selten gesehen, wie Stadien zusammenbrachen, aber ich musste in der Halbzeit so oft in der Schlange stehen. Das heißt, das Toilettenproblem ist wohl komplexer, und wenn die Ingenieure weniger Zeit für die Sicherheit aufwenden können, sind sie möglicherweise in der Lage, das Toilettenproblem zu lösen.

Ihr zweiter Punkt ist irrelevant, weil wir uns bereits darauf geeinigt haben, dass Absolutes ein dummes Unterfangen ist und weil Hank Moody sagt, dass sie nicht existieren (aber ich kann keinen Hinweis dafür finden).


+1 für eine gute Erklärung meines ersten Punktes und für den Verweis auf Hank Moody. Herrlich.
CesarGon

2
Danke ich schätze das. Ich betrachte TDD eher als ein psychologisches Phänomen als als einen technischen Ansatz / Prozess. Aber das ist nur meine Weltanschauung.
Filip Dupanović

Können Sie genau wissen, wie viele Toiletten und wo sie aufgestellt werden sollen? Die Antwort lautet: Ja - fragen Sie jeden Architekten und er wird Ihnen sagen, dass diese Informationen im Voraus erstellt wurden und manchmal mit eindeutigen statistischen Daten, um sie zu sichern.
Gbjbaanb

1

TDD in der Softwareentwicklung ist eine bewährte Methode, ebenso wie die Fehlerbehandlung in Anwendungen eine bewährte Methode sowie die Protokollierung und Diagnose (obwohl dies Teil der Fehlerbehandlung ist).

TDD darf nicht als Werkzeug verwendet werden, um die Softwareentwicklung auf Trial & Error-Codierung zu reduzieren. Dennoch starren die meisten Programmierer auf Laufzeitprotokolle, beobachten Ausnahmen im Debugger oder verwenden andere Anzeichen für Fehler / Erfolg während ihrer Entwicklungsphase, die aus dem Codieren / Kompilieren / Ausführen der App besteht - den ganzen Tag lang.

TDD ist nur eine Möglichkeit, diese Schritte zu formalisieren und zu automatisieren, um Sie als Entwickler produktiver zu machen.

1) Sie können Softwareentwicklung nicht mit Brückenkonstruktion vergleichen, die Flexibilität bei der Brückenkonstruktion entspricht in keiner Weise der Entwicklung eines Softwareprogramms. Das Erstellen der Brücke ist so, als würde man immer wieder dasselbe Programm in eine verlustbehaftete Maschine schreiben. Bridges können nicht wie Software dupliziert und wiederverwendet werden. Jede Brücke ist ein Unikat und muss hergestellt werden. Das gilt auch für Autos und andere Designs.

Das Schwierigste in der Softwareentwicklung ist die Reproduktion von Fehlern. Wenn eine Brücke ausfällt, ist es normalerweise sehr einfach zu bestimmen, was schief gelaufen ist, und theoretisch ist es einfach, den Fehler zu reproduzieren. Wenn ein Computerprogramm ausfällt, kann es sich um eine komplexe Ereigniskette handeln, die das System in einen fehlerhaften Zustand versetzt hat, und es kann sehr schwierig sein, festzustellen, wo der Fehler liegt. TDD- und Unit-Test erleichtern das Testen der Robustheit von Softwarekomponenten, Bibliotheken und Algorithmen.

2) Die Verwendung schwacher Komponententests und flacher Testfälle, die das System nicht belasten, um ein falsches Gefühl des Vertrauens aufzubauen, ist nur eine schlechte Praxis. Das Ignorieren der architektonischen Qualität eines Systems und das Erfüllen der Tests sind natürlich genauso schlecht. Aber auf dem Bauplatz für einen Wolkenkratzer oder eine Brücke zu schummeln, um Material zu sparen und den Bauplänen nicht zu folgen, ist genauso schlimm und passiert die ganze Zeit ...


Ich bin mit Ihrer Annahme nicht einverstanden, dass es in physischen Systemen (dh Systemen, die keine Software sind) einfach ist, Fehler zu reproduzieren. Schauen Sie sich zum Beispiel die äußerst komplexe und harte Arbeit an, die erforderlich ist, um die Ursachen für mechanische Ausfälle bei Flugunfällen zu ermitteln .
CesarGon

Hm, jetzt vergleichen Sie ein abstürzendes Verkehrsflugzeug mit einer ausfallenden Brücke, eine Brücke kann normalerweise nicht fliegen, bei geschlossenem Koffer. Aber der Vergleich zwischen Flugzeugen und Software ist manchmal gültig. Beide Bereiche sind sehr komplex und erfordern eine strukturierte Testmethodik. Wenn eine Brücke ausfällt, wissen Sie, dass sie überlastet ist. Wenn ein Flugzeug abstürzt, wissen Sie, dass der abnormale Zustand des Fliegens über der Erde fehlgeschlagen ist, aber der Grund erfordert normalerweise eine gründliche Untersuchung, ebenso wie ein Softwarefehler.
Ernelli,

Brücken können dupliziert werden - oder zumindest der Brückenentwurf, den Sie vom Architekten kaufen, kann grob modifiziert werden, um genau Ihren Umständen zu entsprechen. Der Punkt ist, dass, wenn Sie eine Brücke benötigen, Sie zum Architekten gehen und er Ihnen eine Liste von nur wenigen Typen gibt, die Sie haben können - Aufhängung, Kasten, Bogen usw., und eine begrenzte Liste von Materialien, aus denen Sie sie bauen können.
gbjbaanb

1

Wenn Sie akzeptieren, dass die Kosten für die Fehlerbehebung umso geringer sind, je früher Fehler gefunden werden, dann macht sich TDD allein schon dadurch bezahlt.


1
Haben Sie Hinweise darauf, dass Fehler in einer TDD-Einstellung früher gefunden werden? Was ist mit den Nebenwirkungen von TDD, wie z. B. Auswirkungen auf die Architektur?
CesarGon

0

Bei TDD geht es nicht wirklich um Tests. Und es ist sicherlich kein Ersatz für gute Tests. Sie erhalten ein durchdachtes Design , das für den Verbraucher leicht zu konsumieren und später einfach zu warten und zu überarbeiten ist. Diese Dinge wiederum führen zu weniger Fehlern und einem besseren, anpassungsfähigeren Softwaredesign. TDD hilft Ihnen auch dabei, Ihre Annahmen zu überdenken und zu dokumentieren, wobei Sie häufig feststellen, dass einige davon falsch waren. Sie finden dies sehr früh im Prozess heraus.

Und als netter Nebeneffekt haben Sie eine große Reihe von Tests, die Sie ausführen können, um sicherzustellen, dass ein Refactoring das Verhalten (Ein- und Ausgaben) Ihrer Software nicht verändert.


6
-1. Viele Leute sagen das immer wieder, aber ich muss erst noch die Magie erkennen, die es möglich macht.
Bart van Ingen Schenau

@ Bart van Ingen Schenau, hast du TDD gemacht? Ich mache es seit ungefähr 4 Jahren und ich habe definitiv gesehen, wie die "Magie" passiert.
Marcie

0

Ich gebe dir eine kurze Antwort. Normalerweise wird TDD genauso falsch betrachtet wie Unit-Tests. Ich habe Unit-Tests erst kürzlich verstanden, nachdem ich mir ein gutes Tech-Talk-Video angesehen hatte. Grundsätzlich sagt TDD nur, dass Sie die folgenden Dinge ARBEITEN möchten. Sie MÜSSEN umgesetzt werden. Dann gestalten Sie den Rest der Software wie gewohnt.

Es ähnelt dem Schreiben von Use Cases für eine Bibliothek vor dem Entwerfen der Bibliothek. Es sei denn, Sie können den Anwendungsfall in einer Bibliothek ändern und möglicherweise nicht für TDD (ich verwende TDD für das API-Design). Sie werden auch dazu ermutigt, weitere Tests hinzuzufügen und über wilde Inputs / Verwendungen nachzudenken, die der Test möglicherweise erhält. Ich finde es nützlich, wenn Sie Bibliotheken oder APIs schreiben, bei denen Sie wissen müssen, dass Sie etwas kaputt gemacht haben, wenn Sie etwas ändern. In den meisten alltäglichen Programmen stört mich das nicht, denn warum brauche ich einen Testfall für einen Benutzer, der eine Taste drückt, oder wenn ich eine CSV-Liste oder eine Liste mit einem Eintrag pro Zeile akzeptieren möchte ... Das ist eigentlich egal, was ich zulasse Um es zu ändern, sollte ich TDD nicht verwenden.


0

Software ist organisch, wenn Tragwerksplanung konkret ist.

Wenn Sie Ihre Brücke bauen, bleibt sie eine Brücke und es ist unwahrscheinlich, dass sie sich innerhalb kurzer Zeit zu etwas anderem entwickelt. Verbesserungen werden über Monate und Jahre vorgenommen, jedoch nicht über Stunden und Tage wie bei Software.

Wenn Sie isoliert testen, gibt es normalerweise zwei Arten von Frameworks, die Sie verwenden können. Eingeschränkter Rahmen und uneingeschränkt. Mit uneingeschränkten Frameworks (in .NET) können Sie unabhängig von den Zugriffsmodifikatoren alles testen und ersetzen. Das heißt, Sie können private und geschützte Komponenten stummschalten und verspotten.

Die meisten Projekte, die ich gesehen habe, verwenden eingeschränkte Frameworks (RhinoMocks, NSubstitute, Moq). Wenn Sie mit diesen Frameworks testen, müssen Sie Ihre Anwendung so gestalten, dass Sie zur Laufzeit Abhängigkeiten einfügen und ersetzen können. Dies impliziert, dass Sie ein lose gekoppeltes Design haben müssen. Locker gekoppeltes Design (wenn es richtig gemacht wird) impliziert eine bessere Trennung von Bedenken, was eine gute Sache ist.

Zusammenfassend kann ich sagen, dass das Denken dahinter darin besteht, dass Ihr Design, wenn es testbar ist, daher lose gekoppelt ist und eine gute Trennung der Bedenken aufweist.

Nebenbei bemerkt, ich habe Anwendungen gesehen, die wirklich testbar, aber aus objektorientierter Designperspektive schlecht geschrieben waren.


0

Warum funktioniert TDD?

Das tut es nicht.

Klarstellung: Automatisierte Tests sind besser als keine Tests. Ich persönlich halte die meisten Komponententests jedoch für Verschwendung, da sie in der Regel tautologisch sind (dh Dinge, die sich aus dem tatsächlich getesteten Code ergeben) und nicht ohne Weiteres nachgewiesen werden können, dass sie konsistent und nicht redundant sind und alle Grenzfälle abdecken (in denen normalerweise Fehler auftreten) ).

Und das Wichtigste: Gutes Softwaredesign fällt nicht auf magische Weise aus Tests heraus, da es von vielen agilen / TDD-Evangelisten beworben wird. Alle, die etwas anderes behaupten, geben bitte Links zu von Experten geprüften wissenschaftlichen Forschungsergebnissen an, die dies belegen, oder verweisen zumindest auf ein Open-Source-Projekt, bei dem die Vorteile von TDD möglicherweise anhand der bisherigen Codeänderungen untersucht werden können.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.