Testgetriebene Entwicklung
Testgetriebene Entwicklung (Tdd) ist ein Softwareentwicklungsprozess Verlassen der Softwareanforderungen, die auf konvertiert werden Testfälle Bevor die Software vollständig entwickelt ist und die gesamte Softwareentwicklung nachverfolgt wird, indem die Software wiederholt gegen alle Testfälle getestet wird. Dies ist im Gegensatz dazu, dass die Software zuerst entwickelt wird, und Testfälle, die später erstellt wurden.
Softwareentwickler Kent Beck, wer wird zugeschrieben, dass er entwickelt oder "wiederentdeckt" hat[1] Die im Jahr 2003 angegebene Technik, dass TDD einfache Designs fördert und das Vertrauen inspiriert.[2]
Die testgetriebene Entwicklung bezieht sich auf die Test-ersten Programmierkonzepte von extremes Programmieren, begonnen 1999,[3] In jüngerer Zeit hat sich jedoch ein allgemeineres Interesse für sich genommen.[4]
Programmierer wenden das Konzept auch auf die Verbesserung an und Debuggen Legacy -Code entwickelt mit älteren Techniken.[5]
Testgetriebener Entwicklungszyklus

Die folgende Sequenz basiert auf dem Buch Testgetriebene Entwicklung mit gutem Beispielsweise:[2]
- 1. Fügen Sie einen Test hinzu
- Das Hinzufügen einer neuen Funktion beginnt mit dem Schreiben eines Tests, der besteht IFF Die Spezifikationen der Feature werden erfüllt. Der Entwickler kann diese Spezifikationen entdecken, indem er nach Fragen fragt Anwendungsfälle und benutzergeschichten. Ein wesentlicher Vorteil der Testentwicklung besteht darin, dass sich der Entwickler auf Anforderungen konzentriert Vor Schreibcode. Dies steht im Gegensatz zu der üblichen Praxis, bei der Unit -Tests nur geschrieben werden nach Code.
- 2. Führen Sie alle Tests aus. Der neue Test sollte scheitern aus erwarteten Gründen
- Dies zeigt, dass für die gewünschte Funktion tatsächlich neuer Code benötigt wird. Es bestätigt, dass die Testkabelbaum funktioniert richtig. Es schließt die Möglichkeit aus, dass der neue Test fehlerhaft ist und immer bestehen wird.
- 3. Schreiben Sie den einfachsten Code, der den neuen Test besteht
- Unelegant oder Hartes Code ist akzeptabel, solange es den Test besteht. Der Code wird in Schritt 5 sowieso geschliffen. Es sollte kein Code über die getestete Funktionalität hinaus hinzugefügt werden.
- 4. Alle Tests sollten jetzt bestehen
- Wenn irgendein ausfällt, muss der neue Code bis zum Verabschiedung überarbeitet werden. Dies stellt sicher, dass der neue Code dem trifft Testanforderungen und brechen nicht vorhandene Funktionen.
- 5. Refactor nach Bedarf, indem Sie Tests nach jedem Refaktor verwenden, um sicherzustellen, dass die Funktionalität erhalten bleibt
- Code ist refaktiert zum Lesbarkeit und Wartbarkeit. Insbesondere sollten hartcodierte Testdaten entfernt werden. Durch das Ausführen der Testsuite nach jedem Refaktor wird sichergestellt, dass keine vorhandene Funktionalität gebrochen wird.
- Beispiele für das Refactoring:
- Bewegen Code dorthin, wo er am logischsten gehört
- Entfernen Doppelter Code
- Herstellung Namen Selbstdokumentation
- Spaltmethoden in kleinere Stücke aufgeteilt
- neu arrangieren Vererbungshierarchien
- Beispiele für das Refactoring:
- Wiederholen
- Der obige Zyklus wird für jedes neue Stück Funktionalität wiederholt. Die Tests sollten klein und inkrementell sein und häufig gemacht. Auf diese Weise kann der Programmierer einfach, wenn neuer Code einige Tests fehlschlägt, einfach rückgängig machen oder eher zurückkehren als debuggen übermäßig. Beim Benutzen externe BibliothekenEs ist wichtig, keine Tests zu schreiben, die so klein sind, dass sie effektiv nur die Bibliothek selbst testen.[4] Es sei denn, es gibt einen Grund zu der Annahme, dass die Bibliothek fehlerhaft ist oder nicht reich genug ist, um alle Bedürfnisse der in der Entwicklung befindlichen Software zu erfüllen.
Entwicklungsstil
Es gibt verschiedene Aspekte bei der Verwendung der testgetriebenen Entwicklung, zum Beispiel die Prinzipien von "Keep It einfach, dumm" (KUSS) und "Du wirst es nicht brauchen"(Yagni). Indem Sie sich darauf konzentrieren, nur den Code zu schreiben, der für die Bestätigung von Tests erforderlich ist, können Designs häufig sauberer und klarer sein als mit anderen Methoden.[2] Im Testgetriebene Entwicklung mit gutem Beispielsweise, Kent Beck schlägt auch das Prinzip vor "Fälsche es, bis du es machst".
Um ein fortschrittliches Designkonzept wie a zu erreichen DesignmusterEs werden Tests geschrieben, die dieses Design generieren. Der Code kann einfacher bleiben als das Zielmuster, bestehen jedoch weiterhin alle erforderlichen Tests. Dies kann zunächst beunruhigend sein, aber es ermöglicht dem Entwickler, sich nur auf das zu konzentrieren, was wichtig ist.
Zuerst das Schreiben der Tests: Die Tests sollten vor der getesteten Funktionalität geschrieben werden. Es wurde behauptet, viele Vorteile zu haben. Es hilft sicherzustellen, dass die Anwendung auf Testbarkeit geschrieben wird, da die Entwickler überlegen müssen, wie die Anwendung von Anfang an testen, anstatt sie später hinzuzufügen. Es stellt auch sicher, dass Tests für jede Funktion geschrieben werden. Das Schreiben der Tests führt zuerst zu einem tieferen und früheren Verständnis der Produktanforderungen, sorgt für die Wirksamkeit des Testcode und behält einen kontinuierlichen Fokus auf Softwarequalität.[6] Beim Schreiben von Feature-First-Code besteht eine Tendenz von Entwicklern und Organisationen, den Entwickler zum nächsten Feature zu bringen und sogar die Tests vollständig zu vernachlässigen. Der erste TDD -Test kompiliert möglicherweise zunächst nicht einmal, da die von ihnen benötigten Klassen und Methoden möglicherweise noch nicht vorhanden sind. Trotzdem fungiert dieser erste Test als Beginn einer ausführbaren Spezifikation.[7]
Jeder Testfall schlägt zunächst fehl: Dies stellt sicher, dass der Test wirklich funktioniert und einen Fehler aufnehmen kann. Sobald dies gezeigt wird, kann die zugrunde liegende Funktionalität implementiert werden. Dies hat zu dem "Test-gesteuerten Entwicklungsmantra" geführt, das "rot/grün/refaktor" ist, wo Rot bedeutet scheitern und grüne Mittel passieren. Die testgetriebene Entwicklung wiederholt ständig die Schritte zum Hinzufügen von Testfällen, die fehlschlagen, übergeben und neu aufgestellt werden. Das Erhalten der erwarteten Testergebnisse in jeder Phase verstärkt das mentale Modell des Entwicklers des Codes, steigert das Vertrauen und erhöht die Produktivität.
Halten Sie das Gerät klein
Für TDD wird eine Einheit am häufigsten als Klasse oder eine Gruppe verwandter Funktionen definiert, die häufig als Modul bezeichnet werden. Einen relativ gering zu halten, wird behauptet, kritische Vorteile zu bieten, einschließlich:
- Reduzierter Debugging -Aufwand - Wenn Testversagen erkannt werden, hilft kleinere Einheiten bei der Verfolgung von Fehlern.
- Selbstdokumentationstests-Kleine Testfälle sind leichter zu lesen und zu verstehen.[6]
Fortgeschrittene Praktiken der testgetriebenen Entwicklung können zu Akzeptanztest -steuerte Entwicklung (ATDD) und Spezifikation mit Beispiel Wenn die vom Kunden angegebenen Kriterien in Akzeptanztests automatisiert werden, die dann den UTDD-Prozess (traditioneller testgesteuerter Entwicklungsbetrieb) (UTDD) vorantreiben.[8] Dieser Prozess stellt sicher, dass der Kunde über einen automatisierten Mechanismus verfügt, um zu entscheiden, ob die Software ihre Anforderungen entspricht. Mit ATDD hat das Entwicklungsteam nun ein spezifisches Ziel - die Akzeptanztests -, die es kontinuierlich auf das konzentrieren, was der Kunde von jeder Benutzergeschichte wirklich will.
Empfohlene Vorgehensweise
Teststruktur
Effektives Layout eines Testfalles stellt sicher, dass alle erforderlichen Maßnahmen abgeschlossen sind, die Lesbarkeit des Testfalls verbessert und den Ausführungfluss glättet. Die konsistente Struktur hilft beim Aufbau eines Selbstdokumentierungstestfalles. Eine häufig angewandte Struktur für Testfälle hat (1) Setup, (2) Ausführung, (3) Validierung und (4) Reinigung.
- Setup: Stellen Sie das Gerät (UUT) oder das Gesamttestsystem in den Status ein, der für den Test erforderlich ist.
- Ausführung: Trigger/Drive das UUT, um das Zielverhalten durchzuführen und alle Ausgaben zu erfassen, z. B. Rückgabewerte und Ausgabeparameter. Dieser Schritt ist normalerweise sehr einfach.
- Validierung: Stellen Sie sicher, dass die Ergebnisse des Tests korrekt sind. Diese Ergebnisse können explizite Ausgänge umfassen, die während der Ausführung oder im Zustandsänderungen in der UUT erfasst wurden.
- Reinigung: Wiederherstellen Sie die UUT oder das Gesamttestsystem im Vor-Test-Zustand. Diese Restaurierung ermöglicht einen weiteren Test, um unmittelbar nach diesem auszuführen. In einigen Fällen sollte die Reinigung der Test gleich vor dem Setup des Tests beginnen, um die Informationen für eine mögliche Testausfallanalyse beizubehalten. [6]
Individuelle Best Practices
Einige Best Practices, die eine Person befolgen könnte Testen Sie Orakel konzentrierte sich nur auf die Ergebnisse, die zur Validierung seines Tests erforderlich sind und zeitbezogene Tests entwerfen, um Toleranz für die Ausführung in nicht realen Zeitbetriebssystemen zu ermöglichen. Die übliche Praxis, eine 5-10-prozentige Marge für die verspätete Ausführung zuzulassen, verringert die potenzielle Anzahl falscher Negativen bei der Testausführung. Es wird auch vorgeschlagen, den Testcode mit dem gleichen Respekt wie der Produktionscode zu behandeln. Der Testcode muss sowohl für positive als auch für negative Fälle korrekt funktionieren, lange Zeit und lesbar und wartbar sein. Teams können sich mit Tests und Testpraktiken zusammenschließen und sie überprüfen, um effektive Techniken auszutauschen und schlechte Gewohnheiten zu fangen.[9]
Praktiken zum Vermeiden oder "Anti-Muster"
- Die Testfälle hängen von Systemstatus ab, die aus zuvor ausgeführten Testfällen manipuliert werden (d. H. Sie sollten immer einen Unit-Test aus einem bekannten und vorkonfigurierten Zustand starten).
- Abhängigkeiten zwischen Testfällen. Eine Testsuite, bei der Testfälle voneinander abhängig sind, ist spröde und komplex. Ausführungsreihenfolge sollte nicht vermutet werden. Basic Refactoring der anfänglichen Testfälle oder die Struktur des UUT führt zu einer Spirale von zunehmend allgegenwärtigen Auswirkungen in den damit verbundenen Tests.
- Interdependente Tests. Voneinander abhängige Tests können kaskadierende falsche Negative verursachen. Ein Versagen in einem frühen Testfall bricht einen späteren Testfall aus, auch wenn in der UUT kein tatsächlicher Fehler vorhanden ist, was die Analyse der Defekte und Debug -Bemühungen erhöht.
- Testen des präzisen Ausführungsverhaltens Timing oder Leistung.
- Bau "allwissende Orakel". Ein Orakel, das mehr als nötig inspiziert, ist im Laufe der Zeit teurer und spröder. Dieser sehr häufige Fehler ist gefährlich, da er über das komplexe Projekt eine subtile, aber allgegenwärtige Zeit sinkt.[9][Klarstellung erforderlich]
- Testen von Implementierungsdetails.
- Langsame Lauftests.
Vorteile
Eine Studie aus dem Jahr 2005 ergab, dass die Verwendung von TDD mehr Tests und wiederum Programmierer, die mehr Tests geschrieben haben, tendenziell produktiver waren.[10] Hypothesen in Bezug auf die Codequalität und eine direktere Korrelation zwischen TDD und Produktivität waren nicht schlüssig.[11]
Programmierer verwenden reine TDD auf neu ("Greenfield") Projekte berichteten, sie hatten nur selten das Bedürfnis, a aufzurufen Debugger. Verwendet in Verbindung mit a VersionskontrollsystemWenn Tests unerwartet ausfallen, kann der Code in die letzte Version zurückkehren, die alle Tests bestanden haben, häufig produktiver als das Debuggen.[12]
Die testgetriebene Entwicklung bietet mehr als nur eine einfache Validierung der Korrektheit, kann aber auch das Design eines Programms vorantreiben.[13] Indem man sich zuerst auf die Testfälle konzentriert, muss man sich vorstellen, wie die Funktionalität von Kunden verwendet wird (im ersten Fall die Testfälle). Der Programmierer befasst sich also vor der Implementierung mit der Schnittstelle. Dieser Vorteil ergänzt sich zu Entwurf durch Vertrag Da nähert es sich eher durch Testfälle als durch mathematische Behauptungen oder Vorurteile.
Die testgesteuerte Entwicklung bietet bei Bedarf kleine Schritte. Es ermöglicht einem Programmierer, sich auf die jeweilige Aufgabe zu konzentrieren, da das erste Ziel darin besteht, den Testpass zu erstellen. Außergewöhnliche Fälle und Fehlerbehebung werden anfangs nicht berücksichtigt, und Tests zur Schaffung dieser fremden Umstände werden separat implementiert. Die testgetriebene Entwicklung stellt auf diese Weise sicher, dass alle schriftlichen Code von mindestens einem Test abgedeckt werden. Dies gibt dem Programmierteam und den nachfolgenden Benutzern ein größeres Vertrauen in den Code.
Während es wahr ist, dass mit TDD mehr Code erforderlich ist als ohne TDD aufgrund des Unit -Testcodees, könnte die Gesamtcode -Implementierungszeit basierend auf einem Modell von Müller und Padberg kürzer sein.[14] Eine große Anzahl von Tests trägt dazu bei, die Anzahl der Fehler im Code zu begrenzen. Die frühe und häufige Natur der Tests hilft, zu Beginn des Entwicklungszyklus Defekte zu fangen und sie daran zu hindern, endemische und teure Probleme zu werden. Das Beseitigen von Mängel frühzeitig wird in der Regel ein langes und mühsames Debuggen später im Projekt vermieden.
TDD kann zu modularisierteren, flexibleren und erweiterbaren Code führen. Dieser Effekt kommt häufig zustande, da die Methodik erfordert, dass die Entwickler an die Software in Bezug auf kleine Einheiten denken, die später geschrieben und getestet werden können. Dies führt zu kleineren, fokussierteren Klassen, lockerer Kupplungund sauberere Schnittstellen. Die Verwendung der Verwendung der Scheinobjekt Das Entwurfsmuster trägt auch zur Gesamtmodularisierung des Codes bei, da dieses Muster erforderlich ist, damit der Code so geschrieben wird, dass Module für die Bereitstellung einfach zwischen Scheinversionen für Unit -Tests und "reale" Versionen umgestellt werden können.
Da nicht mehr Code geschrieben wird als erforderlich, um einen fehlgeschlagenen Testfall zu bestehen, tendieren automatisierte Tests dazu, jeden Codepfad abzudecken. Zum Beispiel, damit ein TDD -Entwickler eine hinzufügt anders
Zweig zu einem bestehenden Abstand wenn
Aussage, der Entwickler müsste zunächst einen fehlgeschlagenen Testfall schreiben, der den Zweig motiviert. Infolgedessen sind die automatisierten Tests, die aus TDD resultieren, tendenziell sehr gründlich: Sie erkennen unerwartete Änderungen im Verhalten des Codes. Dies erfasst Probleme, die auftreten können, wenn eine Veränderung später im Entwicklungszyklus die andere Funktionalität unerwartet verändert.
Madeyski[15] lieferte empirische Beweise (über eine Reihe von Laborversuche mit über 200 Entwicklern) hinsichtlich der Überlegenheit der TDD-Praxis gegenüber dem herkömmlichen Test-Last-Ansatz oder Tests für die Korrektheit, in Bezug auf die niedrigere Kopplung zwischen Objekten (CBO). Die mittlere Effektgröße stellt einen mittleren (aber nahezu großen) Effekt auf der Grundlage der Metaanalyse der durchgeführten Experimente dar, was ein wesentlicher Befund darstellt. Es deutet auf eine bessere Modularisierung (d. H. Ein modulareres Design), eine einfachere Wiederverwendung und Prüfung der entwickelten Softwareprodukte aufgrund der TDD -Programmierpraxis hin.[15] Madeyski hat auch den Effekt der TDD -Praxis auf Unit -Tests unter Verwendung von Zweigabdeckung (BC) und Mutation Score Indicator (MSI) gemessen.[16][17][18] Dies sind Indikatoren für die Gründlichkeit und die Effektivität der Fehlererkennung von Unit -Tests. Die Effektgröße von TDD auf die Zweigabdeckung war mittelgroß und wird daher als wesentlicher Effekt angesehen.[15] Diese Ergebnisse wurden anschließend durch weitere, kleinere experimentelle Bewertungen von TDD bestätigt.[19][20][21][22]
Einschränkungen
Die testgesteuerte Entwicklung führt in Situationen, in denen vollständige Funktionstests erforderlich sind, um den Erfolg oder Misserfolg aufgrund der umfassenden Verwendung von Unit-Tests zu bestimmen, keine ausreichenden Tests durch.[23] Beispiele dafür sind Benutzeroberflächen, Programme, die mit funktionieren Datenbanken, und einige, die von spezifisch abhängen Netzwerk Konfigurationen. TDD ermutigt Entwickler, die minimale Codemenge in solche Module zu stecken und die Logik, die sich in Testable -Bibliothekscode befindet, mit Fälschungen und Verwendung von Fälschungen und Maximierung Mocks die Außenwelt darstellen.[24]
Die Unterstützung des Managements ist unerlässlich. Ohne die gesamte Organisation der Ansicht, dass die testgetriebene Entwicklung das Produkt verbessern wird, kann das Management das Gefühl haben, dass die Zeitversuche für die Schreiben von Tests verschwendet wird.[25]
Unit-Tests, die in einer testgesteuerten Entwicklungsumgebung erstellt wurden, werden normalerweise vom Entwickler erstellt, der den getesteten Code schreibt. Daher können die Tests blinde Flecken mit dem Code teilen: Wenn beispielsweise ein Entwickler nicht erkennt, dass bestimmte Eingabeparameter überprüft werden müssen, werden diese Parameter höchstwahrscheinlich weder der Test noch der Code überprüfen. Ein weiteres Beispiel: Wenn der Entwickler die Anforderungen für das von ihm entwickelnde Modul falsch interpretiert, sind die Code und die von ihnen geschriebenen Unit -Tests auf die gleiche Weise falsch. Daher bestehen die Tests und geben ein falsches Gefühl der Korrektheit.
Eine hohe Anzahl von bestandenen Unit -Tests kann ein falsches Sicherheitsgefühl bringen, was zu weniger zusätzlichen führt Softwaretest Aktivitäten wie z. Integrationstests und Anforderungsprüfung.
Tests werden Teil des Wartungsaufwands eines Projekts. Schlecht geschriebene Tests, zum Beispiel solche, die hartcodierte Fehlerketten enthalten, sind selbst anfällig für Misserfolg und sind teuer zu warten. Dies ist insbesondere bei fragilen Tests der Fall.[26] Es besteht das Risiko, dass Tests, die regelmäßig Fehlausfälle erzeugen, ignoriert werden, so dass bei einem echten Fehler nicht erkannt wird. Es ist möglich, Tests für niedrige und einfache Wartung zu schreiben, beispielsweise durch Wiederverwendung von Fehlerzeichenfolgen, und dies sollte ein Ziel während der Code Refactoring oben beschriebene Phase.
Das Schreiben und Aufrechterhalten einer übermäßigen Anzahl von Tests kostet die Zeit. Außerdem können flexiblere Module (mit begrenzten Tests) neue Anforderungen akzeptieren, ohne dass die Tests geändert werden müssen. Aus diesen Gründen kann das Testen auf nur extreme Bedingungen oder eine kleine Datenprobe einfacher zu angepasst sein als eine Reihe von detaillierten Tests.
Das Ausmaß der Deckungs- und Testdetails, die während wiederholter TDD-Zyklen erreicht werden, kann zu einem späteren Zeitpunkt nicht leicht neu erstellt werden. Daher werden diese ursprünglichen oder frühen Tests im Laufe der Zeit immer kostbarer. Die Taktik besteht darin, sie früh zu reparieren. Wenn eine schlechte Architektur, ein schlechtes Design oder eine schlechte Teststrategie zu einer späten Veränderung führt, bei der Dutzende vorhandener Tests fehlschlagen, ist es wichtig, dass sie individuell festgelegt werden. Das ledigliche Löschen, Deaktivieren oder Schlüpfer kann sie in der Testabdeckung zu nicht nachweisbaren Löchern führen.
Testgesteuerte Arbeit
Die testgetriebene Entwicklung wurde außerhalb der Softwareentwicklung sowohl in Produkt- als auch in Service-Teams als testgetriebene Arbeit übernommen.[27] Ähnlich wie TDD entwickeln sich Nicht-Software-Teams Qualitätskontrolle (QC) Überprüfungen (normalerweise manuelle Tests anstelle automatisierter Tests) für jeden Aspekt der Arbeit vor Beginn. Diese QC -Überprüfungen werden dann verwendet, um das Design zu informieren und die zugehörigen Ergebnisse zu validieren. Die sechs Schritte der TDD -Sequenz werden mit geringfügigen semantischen Änderungen angewendet:
- "Hinzufügen eines Schecks" ersetzt "einen Test hinzufügen"
- "Alle Schecks ausführen" ersetzt "Alle Tests laufen".
- "Do the Work" ersetzt "Schreiben Sie einen Code"
- "Alle Schecks ausführen" ersetzt "rennen Tests".
- "Reinigen Sie die Arbeit" ersetzt "Refactor Code".
- "Wiederholen"
TDD und ATDD
Die testgetriebene Entwicklung hängt mit, aber unterscheidet sich von Akzeptanztest -steuerte Entwicklung (ATDD).[28] TDD ist in erster Linie ein Entwickler-Tool, um eine gut geschriebene Codeeinheit (Funktion, Klasse oder Modul) zu erstellen, die eine Reihe von Vorgängen korrekt ausführt. ATDD ist ein Kommunikationsinstrument zwischen Kunden, Entwickler und Tester, um sicherzustellen, dass die Anforderungen gut definiert sind. TDD erfordert Testautomatisierung. ATDD nicht, obwohl die Automatisierung bei Regressionstests hilft. In TDD verwendete Tests können häufig aus ATDD -Tests abgeleitet werden, da die Codeeinheiten einen Teil einer Anforderung implementieren. ATDD -Tests sollten vom Kunden lesbar sein. TDD -Tests müssen nicht sein.
TDD und BDD
BDD (Verhaltensgetriebene Entwicklung) kombiniert Praktiken von TDD und von ATDD.[29] Es beinhaltet zuerst die Praxis des Schreibens von Tests, konzentriert sich jedoch auf Tests, die Verhalten beschreiben, anstatt Tests, die eine Implementierungseinheit testen. Werkzeuge wie Jbehave, Gurke, Mspec und Specflow Bereitstellung von Syntaxen, die es Produktbesitzern, Entwicklern und Testingenieuren ermöglichen, das Verhalten zusammen zu definieren, die dann in automatisierte Tests übersetzt werden können.
Code -Sichtbarkeit
Testsuite Code muss eindeutig in der Lage sein, auf den Code zugreifen zu können, den er testet. Andererseits normale Entwurfskriterien wie z. Informationen verstecken sich, Kapselung und die Trennung von Bedenken sollte nicht beeinträchtigt werden. Daher wird der Unit -Testcode für TDD normalerweise innerhalb desselben Projekts geschrieben oder Modul als der Code getestet.
Im objektorientiertes Design Dies bietet immer noch keinen Zugriff auf private Daten und Methoden. Daher kann für Unit -Tests zusätzliche Arbeiten erforderlich sein. Im Java und andere Sprachen kann ein Entwickler verwenden Betrachtung Zu Zugang zu privaten Feldern und Methoden.[30] Alternativ an innere Klasse Kann verwendet werden, um die Unit -Tests durchzuführen, damit die Mitglieder und Attribute der beigefügten Klasse die Sichtbarkeit haben. In dem .NET Framework und einige andere Programmiersprachen, Teilklassen Kann verwendet werden, um private Methoden und Daten für die zugreifenden Tests freizulegen.
Es ist wichtig, dass solche Testhacks nicht im Produktionscode bleiben. Im C und andere Sprachen, Compiler -Richtlinien wie zum Beispiel #if Debug ... #endif
Kann um solche zusätzlichen Klassen und in der Tat alle anderen Test-Code platziert werden, um zu verhindern, dass sie in den freigegebenen Code zusammengestellt werden. Dies bedeutet, dass der freigegebene Code nicht genau das gleiche ist wie das, was Unit getestet wurde. Der regelmäßige Betrieb von weniger, aber umfassenderen, von End-to-End-Integrationstests zum endgültigen Release-Build kann (unter anderem) sicherstellen, dass es keinen Produktionscode gibt, der subtil auf Aspekte des Testkabelbaums beruht.
Es gibt einige Debatten unter den Praktikern von TDD, die in ihren Blogs und anderen Schriften dokumentiert sind, ob es trotzdem ratsam ist, private Methoden und Daten zu testen. Einige argumentieren, dass private Mitglieder ein bloßes Implementierungsdetail sind, das sich ändern kann und dies ohne die Anzahl von Tests zu tun dürfen. Daher sollte es ausreichen, eine Klasse über ihre öffentliche Schnittstelle oder über die Schnittstelle unter der Unterklasse zu testen, die einige Sprachen als "geschützte" Schnittstelle bezeichnen.[31] Andere sagen, dass entscheidende Aspekte der Funktionalität in privaten Methoden implementiert werden können, und sie bieten direkten Vorteilen kleinerer und direkterer Einheiten -Tests.[32][33]
Software für TDD
Es gibt viele Test -Frameworks und Tools, die in TDD nützlich sind.
Xunit Frameworks
Entwickler können computergestützte verwenden Testen von Frameworks, üblicherweise gemeinsam benannt Xunit (die von Sunit abgeleitet werden, die 1998 erstellt wurden), um die Testfälle zu erstellen und automatisch auszuführen. Xunit Frameworks bieten Test-Validierungsfunktionen im Assertion-Stil und Ergebnisberichterstattung. Diese Funktionen sind für die Automatisierung von entscheidender Bedeutung, da sie die Belastung der Ausführungsvalidierung von einer unabhängigen Nachbearbeitungsaktivität in eine in die Testausführung enthalten sind. Das von diesen Test -Frameworks bereitgestellte Ausführungsframework ermöglicht die automatische Ausführung aller Systemtestfälle oder verschiedene Teilmengen zusammen mit anderen Funktionen.[34]
Tippen Sie auf Ergebnisse
Tests-Frameworks können die Ausgabe der Unit-Tests in der Sprache-Agnostic akzeptieren Testen Sie alles Protokoll 1987 geschaffen.
Fälschungen, Mocke und Integrationstests
Unit -Tests werden so benannt, weil sie jeweils testen eine Einheit von Code. Ein komplexes Modul hat möglicherweise tausend Einheiten -Tests und ein einfaches Modul kann nur zehn enthalten. Die für TDD verwendeten Unit -Tests sollten niemals die Prozessgrenzen in einem Programm überschreiten, geschweige denn Netzwerkverbindungen. Dies führt zu Verzögerungen, die den Tests langsam laufen lassen und Entwickler davon abhalten, die gesamte Suite auszuführen. Einführung von Abhängigkeiten von externen Modulen oder Daten auch Wendungen Unit -Tests hinein Integrationstests. Wenn sich ein Modul in einer Kette von miteinander verbundenen Modulen schlecht benimmt, ist nicht so sofort klar, wo nach der Ursache des Versagens gesucht werden muss.
Wenn Code in der Entwicklung auf einer Datenbank, einem Webdienst oder einem anderen externen Prozess oder Dienst beruht, ist es auch eine Chance, eine modularere, testbarere und wiederverwendbarere Code zu entwerfen.[35] Zwei Schritte sind erforderlich:
- Wann immer externer Zugang im endgültigen Design benötigt wird, ist ein Schnittstelle sollte definiert werden, der den verfügbaren Zugang beschreibt. Siehe das Abhängigkeitsinversionsprinzip für eine Diskussion über die Vorteile, dies unabhängig von TDD zu tun.
- Die Schnittstelle sollte auf zwei Arten implementiert werden, von denen einer auf den externen Prozess zugreift, und der andere ist a Fälschung oder Mock. Gefälschte Objekte müssen kaum mehr als eine Nachricht wie "Personenobjekt gespeichert" zu a hinzufügen Spurenprotokoll, gegen die ein Test Behauptung kann ausgeführt werden, um das korrekte Verhalten zu überprüfen. Scheinobjekte unterscheiden sich darin, dass sie selbst enthalten Testbehauptungen Dies kann den Test fehlschlagen, beispielsweise, wenn der Name der Person und andere Daten nicht wie erwartet sind.
Fake- und Mock -Objektmethoden, die Daten zurückgeben, angeblich aus einem Datenspeicher oder Benutzer, können den Testprozess helfen, indem sie immer die gleichen realistischen Daten zurückgeben, auf die sich Tests verlassen können. Sie können auch in vordefinierte Fehlermodi eingestellt werden, damit die Routinen zur Fehlerbehandlung entwickelt und zuverlässig getestet werden können. In einem Fehlermodus kann eine Methode eine ungültige, unvollständige oder zurückgeben Null Antwort oder kann eine werfen Ausnahme. Andere gefälschte Dienste als Datenspeicher können auch in TDD nützlich sein: Ein gefälschter Verschlüsselungsdienst kann die übergebenen Daten tatsächlich nicht verschlüsseln. Ein gefälschter Zufallszahlendienst kann immer zurückgeben. 1. Fälsch- oder Scheinimplementierungen sind Beispiele für Abhängigkeitsspritze.
Ein Test-Double ist eine testspezifische Fähigkeit, die eine Systemfunktion, normalerweise eine Klasse oder Funktion, ersetzt, von der die UUT abhängt. Es gibt zwei Male, bei denen Test -Doppel in ein System eingeführt werden können: Link und Ausführung. Die Link -Zeit -Substitution ist, wenn das Test -Double in das Lastmodul zusammengestellt wird, das zur Validierung von Tests ausgeführt wird. Dieser Ansatz wird in der Regel verwendet, wenn Sie in einer anderen Umgebung als der Zielumgebung ausgeführt werden, für die Doppel für den Hardwareebene -Code für die Kompilierung erforderlich ist. Die Alternative zur Substitution der Linker ist die Laufzeitsubstitution, bei der die tatsächliche Funktionalität während der Ausführung eines Testfalls ersetzt wird. Diese Substitution erfolgt typischerweise durch die Neuzuweisung bekannter Funktionszeiger oder der Ersatz von Objekten.
Test -Doppel sind aus verschiedenen Arten und unterschiedlichen Komplexitäten:
- Dummy - Ein Dummy ist die einfachste Form eines Test -Double. Es erleichtert die Zeitsubstitution der Linker, indem ein Standard -Rückgabewert bereitgestellt wird, wenn dies erforderlich ist.
- Stummel - Ein Stub verleiht einem Dummy eine vereinfachte Logik und liefert unterschiedliche Ausgänge.
- SPY - Ein Spion erfasst und stellt Parameter- und Statusinformationen zur Verfügung, und veröffentlichen Zubehör, um Code für private Informationen zu testen, die eine fortgeschrittenere staatliche Validierung ermöglichen.
- Spotten -Ein Schein wird durch einen einzelnen Testfall angegeben, um das testspezifische Verhalten zu validieren, Parameterwerte zu überprüfen und Sequenzierung aufzurufen.
- Simulator-Ein Simulator ist eine umfassende Komponente, die eine höhere Annäherung an die Zielkapazität bietet (die verdoppelte Sache). Ein Simulator benötigt in der Regel einen erheblichen zusätzlichen Entwicklungsaufwand.[6]
Eine Folge einer solchen Abhängigkeitsinjektion ist, dass der tatsächliche Datenbank oder andere externe Zugriffscode vom TDD-Prozess selbst nie getestet wird. Um Fehler zu vermeiden, die sich daraus ergeben können, sind andere Tests erforderlich, die den testgesteuerten Code mit den "realen" Implementierungen der oben diskutierten Schnittstellen instanziieren. Diese sind Integrationstests und sind ziemlich getrennt von den TDD -Unit -Tests. Es gibt weniger von ihnen, und sie müssen seltener als die Unit -Tests durchgeführt werden. Sie können dennoch mit demselben Test -Framework implementiert werden.
Integrationstests, die alle ändern Persistierter Laden oder die Datenbank sollte immer sorgfältig unter Berücksichtigung des anfänglichen und endgültigen Status der Dateien oder der Datenbank konzipiert werden, selbst wenn ein Test fehlschlägt. Dies wird häufig unter Verwendung einer Kombination der folgenden Techniken erreicht:
- Das
Niederreißen
Methode, die für viele Testrahmen integriert ist. -
versuche ... fangen ... endlich
Ausnahmebehandlung Strukturen, wo verfügbar. - Datenbanktransaktionen wo eine Transaktion atomisch Enthält vielleicht ein Schreiben, eine Lektüre und eine passende Löschung.
- Einen "Snapshot" der Datenbank vor dem Ausführen von Tests und Rollen zum Snapshot nach jedem Testlauf. Dies kann mit einem Framework wie z. B. automatisiert werden Ameise oder Nant oder ein kontinuierliche Integration System wie Tempomat.
- Initialisierung der Datenbank in einen sauberen Zustand Vor Tests anstatt aufzuräumen nach Sie. Dies kann relevant sein, wenn die Reinigung die Diagnose von Testfehlern durch Löschen des endgültigen Status der Datenbank schwierig machen kann, bevor eine detaillierte Diagnose durchgeführt werden kann.
TDD für komplexe Systeme
Das Ausüben von TDD auf großen, herausfordernden Systemen erfordert eine modulare Architektur, genau definierte Komponenten mit veröffentlichten Schnittstellen und disziplinierte Systemschichtung mit Maximierung der Plattformunabhängigkeit. Diese nachgewiesenen Praktiken ergeben eine erhöhte Prüfbarkeit und erleichtern die Anwendung von Build- und Testautomatisierung.[6]
Entwerfen für Testbarkeit
Komplexe Systeme erfordern eine Architektur, die eine Reihe von Anforderungen erfüllt. Eine wichtige Teilmenge dieser Anforderungen umfasst die Unterstützung für die vollständigen und effektiven Prüfung des Systems. Effektives modulares Design ergibt Komponenten, die Merkmale teilen, die für eine effektive TDD essentiell sind.
- Hoher Zusammenhalt Stellen Sie sicher, dass jede Einheit eine Reihe verwandter Funktionen bietet und die Tests dieser Funktionen leichter aufrechterhalten.
- Niedrige Kopplung Ermöglicht jede Einheit isoliert effektiv.
- Veröffentlichte Schnittstellen beschränken den Zugriff auf den Komponenten und dienen als Kontaktpunkte für Tests, erleichtern die Erstellung von Tests und die Gewährleistung der höchsten Treue zwischen Test- und Produktionseinheit -Konfiguration.
Eine Schlüsseltechnik zum Aufbau einer effektiven modularen Architektur ist die Szenario-Modellierung, bei der ein Satz von Sequenzdiagrammen konstruiert wird, wobei sich jeweils auf ein Szenario der Ausführung auf Systemebene konzentriert. Das Szenario -Modell bietet ein hervorragendes Mittel, um die Strategie der Interaktionen zwischen Komponenten als Reaktion auf einen bestimmten Reiz zu erstellen. Jedes dieser Szenario -Modelle dient als reichhaltige Anforderungen für die Dienste oder Funktionen, die eine Komponente bereitstellen muss, und diktiert auch die Reihenfolge, in der diese Komponenten und Dienste miteinander interagieren. Die Szenariomodellierung kann den Bau von TDD -Tests für ein komplexes System erheblich erleichtern.[6]
Tests für große Teams verwalten
In einem größeren System wird der Einfluss der Qualität der schlechten Komponenten durch die Komplexität von Wechselwirkungen vergrößert. Diese Vergrößerung macht die Vorteile von TDD im Zusammenhang mit größeren Projekten noch schneller. Die Komplexität der Gesamtpopulation von Tests kann jedoch zu einem Problem an sich werden und potenzielle Gewinne erodieren. Es klingt einfach, aber ein wichtiger Anfangsschritt besteht darin, zu erkennen, dass der Testcode auch eine wichtige Software ist und mit der gleichen Strenge wie der Produktionscode erstellt und verwaltet werden sollte.
Erstellen und Verwalten der die Architektur Die Testsoftware innerhalb eines komplexen Systems ist genauso wichtig wie die Kernproduktarchitektur. Testfahrer interagieren mit der UUT, Testen Sie Doppel und das Unit -Test -Framework.[6]
Konferenz
Die erste TDD -Konferenz fand im Juli 2021 statt.[36] Konferenzen wurden aufgenommen Youtube[37]
Siehe auch
- Akzeptanzprüfung
- Verhaltensgetriebene Entwicklung
- Entwurf durch Vertrag
- Induktive Programmierung
- Integrationstests
- Liste der Softwareentwicklungsphilosophien
- Liste der Einheiten -Test -Frameworks
- Scheinobjekt
- Programmierung mit gutem Beispielsweise
- Gesundheitsüberprüfung
- Selbstprüfungscode
- Softwaretest
- Testfall
- Transformation Priorität Prämisse
- Unit -Tests
- Kontinuierliche testgetriebene Entwicklung
Verweise
- ^ Kent Beck (11. Mai 2012). "Warum bezieht sich Kent Beck auf die" Wiederentdeckung "der testgetriebenen Entwicklung?". Abgerufen 1. Dezember, 2014.
- ^ a b c Beck, Kent (2002-11-08). Testgetriebene Entwicklung mit gutem Beispielsweise. Vaseem: Addison Wesley. ISBN 978-0-321-14653-3.
- ^ Lee Copeland (Dezember 2001). "Extremes Programmieren". Computerwelt. Archiviert von das Original am 5. Juni 2011. Abgerufen 11. Januar, 2011.
- ^ a b Newkirk, JW und Vorontsov, AA. Testgetriebene Entwicklung in Microsoft .net, Microsoft Press, 2004.
- ^ Federn, M. effektiv mit Legacy Code, Prentice Hall, 2004 arbeiten
- ^ a b c d e f g "Wirksame TDD für komplexe eingebettete Systeme Whitepaper" (PDF). Pathfinder -Lösungen. Archiviert von das Original (PDF) Am 2016-03-16.
- ^ "Agile testgetriebene Entwicklung". Agile Sherpa. 2010-08-03. Archiviert von das Original Am 2012-07-23. Abgerufen 2012-08-14.
- ^ Koskela, L. "Testgetrieben: TDD und Akzeptanz TDD für Java -Entwickler", Manning Publications, 2007
- ^ a b Testgetriebene Entwicklung (TDD) für komplexe Systeme Einführung an Youtube von Pathfinder Solutions
- ^ Erdogmus, Hakan; Morisio, Torchiano. "Über die Wirksamkeit des Test-First-Ansatzes zur Programmierung". Verfahren der IEEE -Transaktionen zum Software -Engineering, 31 (1). Januar 2005. (NRC 47445). Archiviert von das Original Am 2014-12-22. Abgerufen 2008-01-14.
Wir fanden heraus, dass Test-First-Studenten im Durchschnitt mehr Tests geschrieben haben und wiederum Studenten, die weitere Tests geschrieben haben, tendenziell produktiver waren.
- ^ ProfFitt, Jacob. "TDD hat sich als effektiv erwiesen! Oder ist es?". Archiviert von das Original am 2008-02-06. Abgerufen 2008-02-21.
Die Beziehung von TDD zur Qualität ist also bestenfalls problematisch. Seine Beziehung zur Produktivität ist interessanter. Ich hoffe, es gibt eine Follow-up-Studie, da die Produktivitätszahlen für mich einfach nicht sehr gut addieren. Es gibt eine unbestreitbare Korrelation zwischen Produktivität und der Anzahl der Tests, aber diese Korrelation ist in der Nicht-TDD-Gruppe tatsächlich stärker (mit einer einzigen Ausreißer Im Vergleich zu der Hälfte der TDD -Gruppe außerhalb des 95% -Bandes).
- ^ Llopis, Noel (20. Februar 2005). "Durch das aussehende Glas treten: Testgetriebene Spielentwicklung (Teil 1)". Spiele von innen. Abgerufen 2007-11-01.
Vergleiche [TDD] mit dem nicht-testgetriebenen Entwicklungsansatz, Sie ersetzen alle mentalen Überprüfungen und Debugger mit Code, die überprüft, dass Ihr Programm genau das tut, was Sie beabsichtigt haben.
- ^ Mayr, Herwig (2005). Projekt (2., neu Bearb. Auffl. Ed.). München: Fachbuchverl. Leipzig im Carl-Hanser-Verl. p. 239. ISBN 978-3446400702.
- ^ Müller, Matthias M.; Padberg, Frank. "Über den Return on Investment der testgetriebenen Entwicklung" (PDF). Universität Karlsruhe, Deutschland: 6. S2CID 13905442. Archiviert von das Original (PDF) Am 2017-11-08. Abgerufen 2012-06-14.
{{}}
: Journal zitieren erfordert|journal=
(Hilfe) - ^ a b c Madeyski, L. "Testgetriebene Entwicklung - Eine empirische Bewertung der agilen Praxis", Springer, 2010, ISBN978-3-642-04287-4, S. 1-245. Doi: 978-3-642-04288-1
- ^ Der Einfluss der Test-First-Programmierung auf die Zweigabdeckung und Mutationsbewertungsanzeige für Unit-Tests: ein Experiment. von L. madeyski Informations- und Softwaretechnologie 52 (2): 169-184 (2010)
- ^ Auf die Auswirkungen der Paarprogrammierung auf die Gründlichkeit und die Fehlerfindung der Effektivität von Unit-Tests von L. madeyski Profes 2007: 207-221
- ^ Einfluss der Paarprogrammierung auf die Gründlichkeit und die Wirksamkeit der Fehlererkennung von Unit -Testsuiten. von L. madeyski Softwareprozess: Verbesserung und Praxis 13 (3): 281-295 (2008)
- ^ M. Pančur und M. Ciglarič, "Auswirkungen der Testentwicklung auf Produktivität, Code und Tests: Ein kontrolliertes Experiment", Information and Softwaretechnologie, 2011, Vol. 53, Nr. 6, S. 557–573, doi: 10.1016/j.infSof.2011.02.002
- ^ D. Fucci, H. Erdogmus, B. Turhan, M. Oivo und N. Juristo, "Eine Dissektion des testgetriebenen Entwicklungsprozesses: Ist es wirklich von Bedeutung, zuerst oder zu testen?", IEEE-Transaktionen on Software Engineering, 2017, Vol. 43, nein. 7, S. 597–614, doi: 10.1109/tse.2016.2616877
- ^ A. Tosun, O. Dieste Tubio, D. Fucci, S. Vegas, B. Turhan, H. Erdogmus, A. Santos, M. Oivo, K. Toro, J. Jarvinen und N. Juristo, "Eine Branchenexperiment Zu den Auswirkungen der testgetriebenen Entwicklung auf externe Qualität und Produktivität ", Empirical Software Engineering, 2016, Vol. 22, S. 1–43, doi: 10.1007/s10664-016-9490-0
- ^ B. Papis, K. Grochowski, K. Subzda und K. Sijko, "Experimentelle Bewertung der testgetriebenen Entwicklung mit Praktikanten, die an einem realen Industrieprojekt arbeiten", IEEE-Transaktionen zum Software-Engineering, 2020, DOI: 10.1109/TSE.20.30275222222222222222222222222222222222222222222222222222222222222222222222222.
- ^ "Probleme mit TDD". Dalkescientific.com. 2009-12-29. Abgerufen 2014-03-25.
- ^ Hunter, Andrew (2012-10-19). "Sind Unit -Tests überbeansprucht?". Einfache talk.com. Abgerufen 2014-03-25.
- ^ Loughran, Steve (6. November 2006). "Testen" (PDF). HP Laboratorien. Abgerufen 2009-08-12.
- ^ "Fragile Tests".
- ^ Leybourn, E. (2013) Regie der agilen Organisation: Ein schlanker Ansatz für das Geschäftsmanagement. London: IT Governance Publishing: 176-179.
- ^ Deal-Agile-Akzeptanz-Testerentwicklung: Bessere Software durch Zusammenarbeit. Boston: Addison Wesley Professional. 2011. ISBN 978-0321714084.
- ^ "BDD". Abgerufen 2015-04-28.
- ^ Burton, Ross (2003-11-12). "Java -Zugangsschutz für Unit -Tests untergraben". O'Reilly Media, Inc.. Abgerufen 2009-08-12.
- ^ Van Rossum, Guido; Warschau, Barry (5. Juli 2001). "PEP 8 - Stilhandbuch für Python Code". Python Software Foundation. Abgerufen 6. Mai 2012.
- ^ Newkirk, James (7. Juni 2004). "Testen privater Methoden/Mitgliedervariablen - sollten Sie oder nicht Sie nicht". Microsoft Corporation. Abgerufen 2009-08-12.
- ^ Stall, Tim (1. März 2005). "So testen Sie private und geschützte Methoden in .NET". CodeProject. Abgerufen 2009-08-12.
- ^ "Wirksame TDD für komplexe, eingebettete Systeme Whitepaper". Pathfinder -Lösungen. Archiviert von das Original Am 2013-08-20. Abgerufen 2012-11-27.
- ^ Fowler, Martin (1999). Refactoring - Verbesserung des Designs des vorhandenen Code. Boston: Addison Wesley Longman, Inc. ISBN 0-201-48567-2.
- ^ Bunardzic, Alex. "First International Test Driven Development (TDD) Konferenz". TDD -Konferenz. Abgerufen 2021-07-20.
- ^ Erste internationale TDD -Konferenz - Samstag, 10. Juli 2021, archiviert vom Original am 2021-12-21, abgerufen 2021-07-20
Externe Links
- TestDrivende -Entwicklung auf Wikiwikiweb
- Bertrand Meyer (September 2004). "Test oder Spezifikation? Test und Spec? Test von Spec!". Archiviert von das Original Am 2005-02-09.
- Microsoft Visual Studio -Team -Test aus einem TDD -Ansatz
- Schreiben Sie Wartungsabteilungstests, mit denen Sie Zeit und Tränen sparen können
- Verbesserung der Anwendungsqualität mithilfe der testgetriebenen Entwicklung (TDD)
- Test Driven Development Conference