it-swarm.dev

Warum funktioniert TDD?

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

Aus technischer Sicht verwirrt es mich aus zwei Gründen:

  1. Der Ansatz "Test + Refactor bis zum Bestehen schreiben" sieht unglaublich anti-technisch aus. Wenn Bauingenieure diesen Ansatz zum Beispiel für den Brückenbau oder Autodesigner für ihre Autos verwenden würden, würden sie ihre Brücken oder Autos zu sehr hohen Kosten umgestalten, und das Ergebnis wäre ein durcheinandergebrachtes Durcheinander ohne gut durchdachte Architektur . Die "Refactor Till Pass" -Richtlinie wird oft als Auftrag angesehen, die architektonische Gestaltung zu vergessen und alles zu tun , was zur Einhaltung des Tests erforderlich ist . Mit anderen Worten, der Test und nicht der Benutzer legen die Anforderung fest. Wie können wir in dieser Situation gute "Fähigkeiten" in den Ergebnissen garantieren, d. H. Ein Endergebnis, das nicht nur korrekt, sondern auch erweiterbar, robust, benutzerfreundlich, zuverlässig, sicher, sicher usw. ist? Dies ist, was Architektur normalerweise tut.
  2. Tests können nicht garantieren, dass ein System funktioniert. es kann nur zeigen, dass es nicht tut. Mit anderen Worten, Tests können Ihnen zeigen, dass ein System Fehler enthält, wenn es einen Test nicht besteht. Ein System, das alle Tests besteht, ist jedoch nicht sicherer als ein System, das diese Tests nicht besteht. Testabdeckung, Testqualität und andere Faktoren sind hier entscheidend. Die falschen sicheren Gefühle, die ein "all green" -Ergebnis für viele Menschen hervorruft, wurden in der Zivil- und Luftfahrtindustrie als äußerst gefährlich eingestuft, da sie möglicherweise als "das System ist in Ordnung" interpretiert werden, wenn dies 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 "getriebene" Bit in TDD als über das "Test" -Bit. Testen ist vollkommen in Ordnung; Was ich nicht bekomme, ist das Design dadurch voranzutreiben.

Ich würde gerne Antworten sehen, die Gründe 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. Vielen Dank.

93
CesarGon

Ich denke, hier gibt es ein Missverständnis. Beim Software-Design kommt das Design dem Produkt sehr nahe. Im Tiefbau, in der Architektur, ist das Design vom eigentlichen Produkt entkoppelt: Es gibt Blaupausen, die das Design enthalten, die dann zum fertigen Produkt materialisiert werden und die durch großen Zeit- und Arbeitsaufwand voneinander getrennt sind.

TDD testet das Design. Aber auch jedes Autodesign und Gebäudedesign wird getestet. Die Bautechniken werden zuerst berechnet, dann in kleinerem Maßstab und dann in größerem Maßstab getestet, bevor sie in einem realen Gebäude ausgeführt werden. Als sie zum Beispiel die H-Träger und die Last erfanden, versicherten sie, dass dies immer wieder versucht wurde, bevor sie tatsächlich die erste Brücke damit bauten.

Entwürfe von Autos werden auch getestet, indem Prototypen entworfen werden, und ja, sicherlich indem Dinge angepasst werden, die nicht genau richtig sind, 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. Aber jede Neugestaltung eines Autos stützt sich auf Erfahrungen, die aus früheren Erfahrungen gewonnen wurden, und jedes Gebäude verfügt über tausend Jahre Grundlagen über die Bedeutung von Raum, Licht, Isolierung, Festigkeit usw. Details werden sowohl in den Gebäuden geändert als auch verbessert und in Neugestaltungen für neuere.

Auch Teile werden getestet. Vielleicht nicht genau im gleichen Stil wie Software, aber mechanische Teile (Räder, Zünder, Kabel) werden normalerweise gemessen und unter Druck gesetzt, um zu wissen, dass die Größen korrekt sind, keine Anomalien zu erkennen sind usw. Sie können röntgen oder laser- sein. gemessen messen sie auf Steine, um gebrochene zu erkennen, sie könnten tatsächlich in der einen oder anderen Konfiguration getestet werden oder sie zeichnen eine begrenzte Darstellung einer großen Gruppe, um sie wirklich auf die Probe zu stellen.

Das sind alles Dinge, die Sie mit TDD einrichten können.

Testen ist in der Tat keine Garantie. Programme stürzen ab, Autos brechen zusammen und Gebäude machen lustige Dinge, wenn der Wind weht. Aber ... "Sicherheit" ist keine boolesche Frage. Selbst wenn Sie nie alles einbeziehen können, ist es besser, beispielsweise 99% der Eventualitäten abzudecken, als nur 50% abzudecken. Nicht zu testen und dann herauszufinden, dass sich der Stahl nicht gut gesetzt hat und spröde ist und beim ersten Hammerschlag bricht, wenn Sie gerade Ihre Hauptstruktur aufbauen, ist eine reine Geldverschwendung. Dass es andere Bedenken gibt, die das Gebäude noch verletzen könnten, macht es nicht weniger dumm, zuzulassen, dass ein leicht vermeidbarer Fehler Ihr Design beeinträchtigt.

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

66
Inca

IMO, die meisten Erfolgsgeschichten für TDD sind gefälscht und nur für Marketingzwecke. 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 verständlichem Code.

Als wir TDD ausprobierten, schien alles gut zu sein. Ich konnte viele Tests schreiben und die Teile verspotten, die für einen Komponententest schwierig sind. Sobald Sie eine angemessene Menge an Code haben und eine Änderung der Benutzeroberfläche erforderlich ist, werden Sie geschraubt. Viele Tests müssen behoben werden und Sie werden mehr Tests neu schreiben als die tatsächliche Änderung im Code.

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

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

Norvig: Ich sehe Tests eher als eine Möglichkeit, Fehler zu korrigieren, als als eine Art des Designs. Dieser extreme Ansatz zu sagen: „Nun, das erste, was Sie tun, ist, einen Test zu schreiben, der besagt, dass ich am Ende die richtige Antwort bekomme.“ Dann führen Sie ihn aus und sehen, dass er fehlschlägt. Dann sagen Sie: „Was mache ich? brauchen Sie als nächstes? “- das scheint mir nicht der richtige Weg zu sein, etwas zu entwerfen. Nur wenn es so einfach wäre, dass die Lösung vorherbestimmt wäre, wäre das sinnvoll. Ich denke, du musst zuerst darüber nachdenken. Sie müssen sagen: „Was sind die Stücke? Wie kann ich Tests für Stücke schreiben, bis ich weiß, was einige davon sind? “ Und wenn Sie das getan haben, ist es eine gute Disziplin, Tests für jedes dieser Teile durchzuführen und gut zu verstehen, wie sie miteinander und mit den Grenzfällen usw. interagieren. Diese sollten alle Tests haben. Aber ich glaube nicht, dass Sie das gesamte Design steuern, indem Sie sagen: "Dieser Test ist fehlgeschlagen."

26
Navaneeth K N

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 ausfü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 rechnungsunternehmen in dieser Situation DIESES Ergebnis haben sollte.".
  2. [~ # ~] wie [~ # ~] der Code aufgerufen werden soll. Die tatsächlichen Schritte, die zur Durchführung der Tests erforderlich sind, werden direkt ohne externes Gerüst angegeben (Datenbanken werden verspottet usw.).

Sie schreiben zuerst die Ansicht von außen.

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

Dies führt normalerweise auch zu saubererem Code, der weniger erklärende Dokumentation erfordert.

Sie werden schneller erledigt

Da Sie die Spezifikation in ausführbarer Form haben, sind Sie fertig, wenn die vollständige Testsuite bestanden ist. Sie können weitere Tests hinzufügen, wenn Sie die Dinge auf einer detaillierteren Ebene klären, aber als Grundprinzip haben Sie einen sehr klaren und sichtbaren Indikator für den Fortschritt und wann Sie fertig sind.

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

Für diejenigen, die darüber nachdenken, kann es nützlich sein, 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. Wenn Sie fertig sind, steht die ausführbare Spezifikation allen zur Verfügung, die sehen möchten, wie die Bibliothek aufgerufen wird.

Kürzlich durchgeführte Studie

"Die Ergebnisse der Fallstudien zeigen, dass die Defektdichte der vier Produkte vor der Freisetzung 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% anfängliche Entwicklungszeit nach Einführung von TDD. " ~ Ergebnisse und Erfahrungen von 4 Industrieteams

25
user1249

Das Erstellen von Software ist nicht das Schreiben des Codes. Kein Softwareprojekt sollte zuerst ohne einen Plan mit breitem Anwendungsbereich beginnen. Genau wie ein Projekt zur Überbrückung zweier Flussufer zuerst einen solchen Plan benötigt.

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

Im Hochbau sieht es ein bisschen so aus:

'Wir haben diese beiden Metallteile miteinander verbunden, und die Verbindung muss Scherkräfte in der Größenordnung von x aufrechterhalten. Lassen Sie uns testen, welche Verbindungsmethode die beste ist, um dies zu tun. '

Um zu testen, ob Software als Ganzes funktioniert, entwerfen 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 einem Brückenbauunternehmen: "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 bereit zu sein." Dies ist eine Definition von Abnahmetest: Das Unternehmen erhält nicht den vollen Betrag (oder irgendeinen) Geldbetrag, wenn es diesen Test nicht bestehen kann.

  2. Das Management des Unternehmens entscheidet über den Projektplan. Sie bilden Arbeitsteams und Ziele für jedes Team. Wenn die Teams diese Ziele nicht erreichen, wird die Brücke nicht rechtzeitig gebaut. Hier gibt es jedoch 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 anstellt usw., sodass das gesamte Projekt weiterhin das in Punkt 1 festgelegte Ziel erreicht.

  3. Innerhalb eines Teams, das für das Entwerfen bestimmter Brückenkomponenten verantwortlich ist, sieht es wie im obigen Beispiel aus. Manchmal liegt die Lösung auf der Hand, weil wir über ein großes Wissen über das Bauen von Brücken verfügen (es ist wie bei der Verwendung einer gut getesteten Bibliothek in der Softwareentwicklung - Sie gehen 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.

19
Mchl

In meinen Augen funktioniert TDD weil

  • Sie müssen definieren, was das Gerät tun soll, bevor Sie sich für die Implementierung mit einer Genauigkeit entscheiden, die im Allgemeinen nicht durch ein Spezifikations- oder Anforderungsdokument abgedeckt ist
  • Dadurch wird Ihr 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 zu den Punkten, die Sie ansprechen

  • Code ist formbarer als Ziegel oder Stahl, daher ist das Ändern billiger. Es ist immer noch billiger, wenn Sie Tests haben, um sicherzustellen, dass das Verhalten unverändert bleibt
  • TDD ist keine Entschuldigung, kein Design zu machen - eine Architektur auf hoher Ebene wird allgemein immer noch empfohlen, nur nicht mit zu vielen Details. Von Big Up Front Design wird abgeraten, aber es wird empfohlen, gerade genug Design zu machen
  • TDD kann nicht garantieren, dass ein System funktioniert, verhindert jedoch, dass viele kleine Fehler durchrutschen, 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, fehlerhaft zu sein
18
Gavin Clarke

TL; DR

Das Programmieren ist immer noch eine Entwurfsaktivität, es ist keine Konstruktion. Das Schreiben von Unit-Tests nachträglich bestätigt nur, 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 "Onkel Bob" spricht direkt über dieses Problem. Sehr früh im 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 seiner Artikel zum Thema gesammelt werden).

Das Interessante an diesem Argument ist, dass er im Gegensatz zu anderen Ingenieurdisziplinen, in denen das Bauen eine sehr teure Aktivität ist, darauf hinweist, dass das Erstellen von Software relativ kostenlos ist (klicken Sie auf Kompilieren in Ihrer IDE und Sie haben Ihre erstellt) Software). Wenn Sie das Schreiben von Code als Entwurfsaktivität anstelle einer Konstruktionsaktivität betrachten, ist der Rot-Grün-Refaktor-Zyklus im Grunde eine Übung im Entwurf. Ihr Entwurf entwickelt sich, wenn Sie Tests schreiben, den Code, um sie zu erfüllen, und den Refaktor Integrieren Sie den neuen Code in das vorhandene System.

TDD als Spezifikation

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

Schreibe Tests in die Funktionalität

Ein häufiger Fehler beim Testen von Einheiten 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 solche Tests sehen

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

Ich denke, dieser Code könnte nützlich sein (stellen Sie sicher, dass jemand mit einer einfachen Eigenschaft nichts Obszönes getan 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 schrieb der Code, um den Test zu bestehen. Kompilieren, testen ... hat beim neuen Test ein Grün bekommen. Aber ich habe auch bei einem anderen Test ein Rot bekommen, von dem ich nicht erwartet hatte, dass es rot wird.

Als ich den Fehler betrachte, atme ich erleichtert auf, weil ich bezweifle, dass ich diesen Fehler schon seit einiger Zeit hätte, wenn ich diesen Test nicht durchgeführt hätte. Und es war ein sehr böser Fehler. 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 (wobei der Fehler andere Module infizierte, die von diesem Code abhingen), und als der Fehler entdeckt wurde, wäre es eine große Aufgabe gewesen, ihn ordnungsgemäß zu beheben.

Der wahre Vorteil von TDD besteht darin, dass wir Änderungen mit rücksichtsloser Hingabe 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 der gleichen Hinsicht erspart Ihnen TDD, Fehler ohne Kopf in Katastrophen zu verwandeln, die Projekte töten.

16
Michael Brown

Sie werden niemanden finden, der sich für testgetriebene Entwicklung oder sogar testgetriebene Design (sie sind unterschiedlich) einsetzt, der besagt, dass Tests Anwendungen beweisen. Nennen wir das einfach einen Strohmann und fertig.

Sie werden niemanden finden, der TDD nicht mag oder nicht beeindruckt ist und der sagt, dass Tests Zeit- und Arbeitsverschwendung sind. Obwohl Tests keine Anwendungen beweisen, sind sie beim Auffinden von Fehlern sehr hilfreich.

Mit diesen beiden Dingen macht keine Seite etwas anderes in Bezug auf die tatsächliche Durchführung von Tests an der Software. 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 diesem Zeitpunkt entdeckt werden kann. Niemand mit einem halben Hinweis verkauft Software ohne Test und niemand mit einem halben Hinweis 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. In Nicht-TDD-Tests werden nach oder in Übereinstimmung mit der Software geschrieben.

Das Problem, das ich in Bezug auf Letzteres gesehen habe, ist, dass das Testen dann eher auf die Software abzielt, die geschrieben wird, als auf das gewünschte Ergebnis oder die gewünschte Spezifikation. Selbst wenn das Testteam vom Entwicklungsteam getrennt ist, neigt 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 darlegt, was er will, die Entwickler davonlaufen und etwas schreiben und wenn sie zu dem Kunden zurückkehren und "erledigt" sagen. Es stellt sich heraus, dass es absolut NICHT das ist, was der Kunde verlangt hat. "Aber es besteht alle Tests ..."

Das Ziel von TDD ist es, dieses "zirkuläre Argument" zu brechen und die Grundlage für die Tests zu schaffen, bei denen die Software getestet wird, die nicht die Software selbst ist. Die Tests sind so geschrieben, dass sie auf das Verhalten abzielen, das der "Kunde" wünscht. Die Software wird dann geschrieben, um diese Tests zu bestehen.

TDD ist Teil der Lösung, mit der dieses Problem behoben werden soll. Es ist nicht der einzige Schritt, den Sie machen. Sie müssen auch sicherstellen, dass mehr Kundenfeedback und häufiger vorhanden ist.

Nach meiner Erfahrung ist es jedoch sehr schwierig, TDD erfolgreich zu implementieren. 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 bringen. Immer wieder habe ich Leuten in meinem Team gesagt, dass sie die Tests ZUERST schreiben sollen. Ich habe noch nie einen dazu gebracht. Am Ende haben Zeitbeschränkungen und Politik alle Bemühungen zerstört, so dass wir überhaupt keine Unit-Tests mehr durchführen. Dies führte natürlich unweigerlich dazu, dass das Design versehentlich und stark gekoppelt wurde, so dass die Implementierung selbst dann unerschwinglich kostspielig wäre, wenn wir dies wollten. Das zu vermeiden, ist das, was TDD letztendlich für Entwickler bietet.

11
Edward Strange

Zuerst entwerfen

TDD ist nicht eine Ausrede, um Design zu überspringen. Ich habe viele Sprünge im "agilen" Zug gesehen, weil sie, obwohl sie sofort mit dem Codieren beginnen konnten. Mit True Agile können Sie die Statistikcodierung viel schneller durchführen als mit den (anderen Feld-) bewährten Methoden, die den Wasserfallprozess inspiriert haben.

Aber früh testen

Wenn man sagt, dass der Test das Design antreibt, bedeutet dies einfach, dass man Tests sehr früh in der Designphase verwenden kann, lange bevor es abgeschlossen ist. Wenn Sie diese Tests durchführen, wird Ihr Design stark beeinflusst, indem Sie die Grauzonen herausfordern und sie lange vor Fertigstellung des Produkts gegen die reale Welt stellen. 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 bringt TDD den Test einfach als integralen Teil des Designs, anstatt am Ende etwas zu tun, um ihn zu validieren. Wenn Sie TDD immer häufiger verwenden, werden Sie sich darüber im Klaren sein, wie Sie Ihr System beim Entwerfen zerstören/beschädigen können. Persönlich mache ich meine Tests nicht immer zuerst. Sicher, ich mache die offensichtlichen (Einheits-) Tests an einer Schnittstelle, aber die wirklichen Vorteile ergeben sich aus den Integrations- und Spezifikationstests, die ich erstelle, wenn ich über eine neue und kreative Art nachdenke, wie dieses Design brechen kann. Sobald ich mir einen Weg überlege, codiere ich einen Test dafür 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 Hauptbuilds ist (da es weiterhin fehlschlägt).

Wer fährt dann die Show?

In TDD bedeutet das hier gefahrene einfach, dass Ihre Tests Ihr Design so stark beeinflussen, dass man spüren kann, 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 im Laufe der Zeit ein gutes Maß an Vertrauen in das gewinnen, was Sie erstellt haben, und so weiter aufbauen können, wenn Sie wissen, dass es auf soliden Grundlagen beruht.

fest, solange die Tests fest sind

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

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

stoppen Sie genau dort ... Software-Engineering unterscheidet sich sehr von allen anderen Engineering-Praktiken da draußen. Tatsächlich hat Software-Engineering viel mehr mit Literatur zu tun. Man kann ein fertiges Buch nehmen, 4 Kapitel daraus herausreißen und zwei neue Kapitel schreiben, um sie zu ersetzen. Stecke sie wieder in das Buch und du hast 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 bei der Erstellung. Wenn Sie Ihre Tests durchgeführt haben und zugelassen haben, dass sie Ihr Design ausreichend beeinflussen, ist dies möglicherweise sogar billiger als die erstmalige Erstellung, da Sie ein gewisses Maß an Sicherheit haben, dass dieser Ersatz nicht die Abdeckung der Tests beeinträchtigt.

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

Weil das Testen eine ganz andere Denkweise erfordert als das Bauen. Nicht jeder kann zurück und zurück wechseln, tatsächlich werden einige Leute nicht in der Lage sein, richtige 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 (Code-Abdeckung fällt mir ein). Sie werden gerne Pfadtests und Ausnahmetests durchführen, aber die Eckfälle und Randbedingungen vergessen.

Andere verlassen sich nur auf Tests, die ganz oder teilweise auf das Design verzichten. Jedes Mitglied, das es tut, integriert sich dann ineinander. Design ist in erster Linie ein Kommunikationsinstrument, das wir in den Boden gesteckt haben, 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 gesteckt haben. Integration und Zusammenführung werden immer schmerzhaft sein und es werden ihnen Tests auf höchster Abstraktionsebene fehlen.

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

9
Newtopian

Mit TDD neigen Sie dazu, keinen Code zu schreiben, der nicht einfach oder schnell zu testen ist. Dies mag wie eine kleine Sache erscheinen, kann jedoch tiefgreifende Auswirkungen auf ein Projekt haben, da es sich darauf auswirkt, wie einfach es ist, Fehler mit Tests umzugestalten, zu testen, zu reproduzieren und Korrekturen zu überprüfen.

Für einen neuen Entwickler im Projekt ist es auch einfacher, sich auf den neuesten Stand zu bringen, wenn Sie den von Tests unterstützten Code besser berücksichtigt haben.

7
Alb

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 bestehen.

1) Meine erste Annahme ist, dass dies (hauptsächlich) nicht darauf zurückzuführen ist, dass TDD dem Code (als solcher) "bessere Qualität" hinzufügt. Es ist eher so, als ob TDD dabei hilft, die schlimmsten Teile und Gewohnheiten auszumerzen und so indirekt die Qualität zu erhöhen.

Ich würde sogar befürworten, dass es nicht der Test selbst ist - es ist der Prozess von Schreiben diesen Tests. Es ist schwierig, Tests für einen schlechten Code zu schreiben und umgekehrt. Wenn Sie dies während des Programmierens im Hinterkopf behalten, wird viel schlechter Code eliminiert.

2) Ein anderer Gesichtspunkt (dies wird philosophisch) ist das Befolgen der mentalen Gewohnheiten des Meisters. Du lernst nicht, ein Meister zu werden, indem du seinen "äußeren Gewohnheiten" folgst (wie ein langer Bart ist gut), du musst seine inneren Denkweisen lernen, und das ist schwer. Und irgendwie (Anfänger) dazu bringen, TDD zu folgen und ihre Denkweise näher an die des Meisters anzupassen.

5
Maglob

Der Ansatz "Test + Refactor bis zum Bestehen schreiben" sieht unglaublich anti-technisch aus.

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

Code Refactoring ist der Prozess des Änderns des Quellcodes eines Computerprogramms, ohne dessen externes Funktionsverhalten zu ändern, um einige der nicht funktionalen Attribute der Software zu verbessern.

Daher können Sie nicht refactor Code, bis es vorbei ist.

Und bei TDD, insbesondere bei Unit-Tests (die 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 Testen von Einheit um Testen von Einheiten 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 Softwareentwickler häufig nicht zutreffen, und es ist sehr pro-Engineering, die Einheiten vor ihrer Verwendung zu testen, da dies die Verwendung getesteter, qualitativ hochwertiger Komponenten bedeutet.
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 erwarten, dass er es als Einheit testet, dh die erforderlichen Spezifikationen definiert (z. B. Gewicht, Durchlässigkeit, Stabilität) usw.) und testen und verfeinern Sie es anschließend, bis es ihnen entspricht.

Deshalb funktioniert TDD. Denn wenn Sie Software für getestete Einheiten erstellen, sind die Chancen viel besser, wenn Sie sie zusammenstecken, und wenn dies nicht der Fall ist, können Sie erwarten, dass das Problem in Ihrem Klebercode enthalten ist, vorausgesetzt, Ihre Tests decken gut ab.

edit:
Refactoring bedeutet: keine Änderung der Funktionalität. Ein Punkt beim Schreiben eines Komponententests besteht darin, sicherzustellen, dass durch Refactoring der Code nicht beschädigt wird. TDD soll also sicherstellen, dass Refactoring keine Nebenwirkungen hat.
Die Granularität ist kein Thema der Perspektive, da, wie gesagt, Unit-Test-Testeinheiten und keine Systeme getestet werden, wobei die Granularität genau definiert ist.

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

Im Gegensatz zum Tiefbau entwickelt sich im Software-Engineering in der Regel ein Projekt 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 Doppeldeckbrücke möchte und ob er diese mit der nächsten Autobahn verbinden möchte und ob er möchte, dass es sich um ein Heben handelt Brücke, weil seine Firma vor kurzem begann, Segelschiffe zu verwenden.
Softwareentwickler haben die Aufgabe , Designs zu ändern. Nicht weil ihre Designs fehlerhaft sind, sondern weil dies der Modus Operandi ist. Wenn die Software gut entwickelt 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, hilft es Ihnen, auf Änderungen der Anforderungen wesentlich schneller und sicherer zu reagieren als ohne.

TDD fügt dem Entwicklungsprozess Anforderungen hinzu, verbietet jedoch keine anderen Methoden zur Qualitätssicherung. Zugegeben, TDD bietet nicht die gleiche Sicherheit wie die formale Verifizierung, aber andererseits ist die formale Verifizierung äußerst kostspielig und auf Systemebene nicht verwendbar. Und dennoch, wenn Sie möchten, können Sie beide kombinieren.

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

Am Ende löst kein Werkzeug tatsächlich ein Problem. Tools erleichtern nur die Lösung eines Problems. Sie können fragen: Wie hilft mir ein Meißel bei großartiger Architektur? Wenn Sie gerade Wände planen, sind gerade Ziegel hilfreich. Und ja, zugegeben, wenn Sie dieses Werkzeug einem Idioten geben, wird er es wahrscheinlich irgendwann durch seinen Fuß schlagen, aber das ist nicht die Schuld des Meißels, so sehr es kein Fehler von TDD ist, dass es Anfängern falsche Sicherheit gibt. die keine guten Tests schreiben.
Unter dem Strich kann man also sagen, dass TDD viel besser funktioniert als kein TDD.

3
back2dos

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

Aus theoretischer Sicht beweisen wir, dass etwas funktioniert, indem wir es anhand von Fehlerpunkten überprüfen. Das ist die verwendete Methode. Es mag viele andere Möglichkeiten geben, 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 nur 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 TDD aus dieser Perspektive nähern, geht es nicht um "Tests schreiben + Refaktor bis zum Bestehen", sondern darum, dass dies abgeschlossen hat. Ich konzentriere mich jetzt ganz auf das nächste Feature als das jetzt am meisten wichtige Sache .

Überlegen Sie, wie dies für den Tiefbau gilt. Wir bauen ein Stadion, das ein öffentliches Publikum von 150000 Menschen aufnehmen kann. Nachdem wir bewiesen haben, dass die strukturelle Integrität des Stadions solide ist, haben wir Sicherheit zuerst erfüllt. Wir können uns jetzt auf andere Themen konzentrieren, die sofort wichtig werden, wie 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 die bestmögliche Benutzererfahrung erzielen, wenn Sie sich auf neue und aufregende Funktionen konzentrieren und gleichzeitig die Integrität bewahren. In beiden Fällen erhalten Sie es auf halbem Weg. Ich meine, wie können Sie genau wissen, wie viele Toiletten und wo sollten Sie für 150000 Menschen platzieren? Ich habe in meinem eigenen Leben selten Stadien zusammenbrechen sehen, aber ich musste so oft während der Halbzeit 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, können sie das Toilettenproblem möglicherweise endlich lösen.

Ihr zweiter Punkt ist irrelevant, weil wir uns bereits darauf geeinigt haben, dass Absolutes ein Narren-Bestreben sind und weil Hank Moody sagt, dass sie nicht existieren (aber ich kann anscheinend keine Referenz dafür finden).

2

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

Schreiben Sie nicht nur die Bibliotheken, aus denen die Basis der Software besteht, sondern 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 Gurke (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 bei Bedarf vorzunehmen. Beginnen Sie mit dem Schreiben einiger Tests und schreiben Sie dann einen kleinen Code, der diese Tests besteht. Fügen Sie weitere Tests hinzu und 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 große Änderungen erfahren, und komplexe Umgestaltungen durchzuführen, ohne dass die Tests zuerst geschrieben werden müssen, ist nur Fragen für Probleme.

2
sevenseacat

Wenn Sie akzeptieren, dass je früher Fehler gefunden werden, desto geringer sind die Kosten für deren Behebung, dann lohnt sich TDD allein.

1
SnoopDougieDoug

TDD in der Softwareentwicklung ist eine gute Praxis, ebenso wie die Fehlerbehandlung in Anwendungen eine gute Praxis ist, ebenso wie 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 während ihrer Entwicklungsphase andere Anzeichen für Fehler/Erfolg, die darin bestehen, die App den ganzen Tag über zu codieren/kompilieren/auszuführen.

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

1) Sie können Software-Engineering nicht mit Brückenbau vergleichen, die Flexibilität beim Brückenbau ist nicht annähernd so groß wie beim Entwurf eines Softwareprogramms. Das Bauen 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. Gleiches gilt 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 festzustellen, 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 Vertrauensgefühl aufzubauen, ist nur eine schlechte Praxis. Die architektonische Qualität eines Systems zu ignorieren und nur die Tests zu erfüllen, ist natürlich genauso schlecht. Aber auf der Baustelle für einen Wolkenkratzer oder eine Brücke zu schummeln, um Material zu sparen und nicht den Bauplänen zu folgen, ist genauso schlimm und es passiert die ganze Zeit ...

1
Ernelli

Ich werde Ihnen eine kurze Antwort geben. In der Regel wird TDD genauso falsch betrachtet wie Unit-Tests. Ich habe Unit-Tests bis vor kurzem nie verstanden, nachdem ich mir ein gutes Tech-Talk-Video angesehen hatte. Im Wesentlichen gibt TDD nur an, dass die folgenden Dinge funktionieren sollen. Sie müssen implementiert werden. Dann entwerfen Sie den Rest der Software wie gewohnt.

Es ist so, als würde man Anwendungsfälle für eine Bibliothek schreiben, bevor man die Bibliothek entwirft. Außer 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 aufgefordert, weitere Tests hinzuzufügen und über wilde Eingaben/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 kümmere ich mich nicht darum, warum ich einen Testfall für einen Benutzer brauche, der eine Taste drückt oder wenn ich eine CSV-Liste oder eine Liste mit einem Eintrag pro Zeile akzeptieren möchte ... Das ist nicht wirklich wichtig, was ich zulasse Um es so zu ändern, sollte ich TDD nicht verwenden.

0
user2528

Warum funktioniert TDD?

Das tut es nicht.

Klarstellung: Automatisierte Tests sind besser als keine Tests. Ich persönlich denke jedoch, dass die meisten Komponententests Verschwendung sind, weil sie normalerweise tautologisch sind (dh Dinge sagen, die aus dem tatsächlich getesteten Code ersichtlich sind), und es kann nicht leicht bewiesen werden, dass sie konsistent, nicht redundant sind und alle Grenzfälle abdecken (in denen normalerweise Fehler auftreten) ).

Und das Wichtigste: Gutes Software-Design fällt nicht auf magische Weise aus Tests heraus, wie 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 des Verlaufs der Codeänderungen untersucht werden können.

0
KolA

Bei TDD geht es nicht wirklich ums Testen. Und es ist sicherlich kein Ersatz für gute Tests. Was es Ihnen gibt, ist ein Design, das gut durchdacht, für den Verbraucher leicht zu konsumieren und später leicht zu warten und umzugestalten ist. Diese Dinge führen wiederum zu weniger Fehlern und einem besseren, anpassungsfähigeren Software-Design. TDD hilft Ihnen auch dabei, Ihre Annahmen zu überdenken und zu dokumentieren, wobei Sie häufig feststellen, dass einige davon falsch waren. Sie finden diese 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 Ausgänge) Ihrer Software nicht verändert.

0
Marcie

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, können Sie normalerweise zwei Arten von Frameworks verwenden. Eingeschränkter Rahmen und uneingeschränkt. Mit uneingeschränkten Frameworks (in .NET) können Sie alles testen und ersetzen, unabhängig von Zugriffsmodifikatoren. Das heißt, Sie können private und geschützte Komponenten stubben 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 bedeutet, 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 glaube ich, dass das Denken dahinter darin besteht, dass wenn Ihr Design testbar ist, es daher lose gekoppelt ist und eine gute Trennung der Bedenken aufweist.

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

0
CodeART