Ich glaube, das ist ein Missverständnis, wie ich es mir vorstellen kann.
Der Testcode, der den Produktionscode testet, ist überhaupt nicht ähnlich. Ich werde in Python demonstrieren:
def multiply(a, b):
"""Multiply ``a`` by ``b``"""
return a*b
Dann wäre ein einfacher Test:
def test_multiply():
assert multiply(4, 5) == 20
Beide Funktionen haben eine ähnliche Definition, aber beide machen sehr unterschiedliche Dinge. Kein doppelter Code hier. ;-);
Es kommt auch vor, dass Personen doppelte Tests schreiben, die im Wesentlichen eine Zusicherung pro Testfunktion enthalten. Das ist Wahnsinn und ich habe Leute gesehen, die das getan haben. Das ist schlechte Praxis.
def test_multiply_1_and_3():
"""Assert that a multiplication of 1 and 3 is 3."""
assert multiply(1, 3) == 3
def test_multiply_1_and_7():
"""Assert that a multiplication of 1 and 7 is 7."""
assert multiply(1, 7) == 7
def test_multiply_3_and_4():
"""Assert that a multiplication of 3 and 4 is 12."""
assert multiply(3, 4) == 12
Stellen Sie sich vor, Sie tun dies für mehr als 1000 effektive Codezeilen. Stattdessen testen Sie pro Feature:
def test_multiply_positive():
"""Assert that positive numbers can be multiplied."""
assert multiply(1, 3) == 3
assert multiply(1, 7) == 7
assert multiply(3, 4) == 12
def test_multiply_negative():
"""Assert that negative numbers can be multiplied."""
assert multiply(1, -3) == -3
assert multiply(-1, -7) == 7
assert multiply(-3, 4) == -12
Wenn nun Features hinzugefügt / entfernt werden, muss ich nur noch eine Testfunktion hinzufügen / entfernen.
Sie haben vielleicht bemerkt, dass ich keine for
Schleifen angewendet habe . Dies liegt daran, dass es gut ist, einige Dinge zu wiederholen. Wenn ich Schleifen angewendet hätte, wäre der Code viel kürzer. Wenn eine Zusicherung jedoch fehlschlägt, kann dies die Ausgabe verschleiern, die eine mehrdeutige Nachricht anzeigt. Wenn dies der Fall ist, dann wird Ihre Tests weniger nützlich und Sie werden einen Debugger müssen prüfen , wo die Dinge schief gehen.
assert multiply(1,3)
würde dies fehlschlagen, aber Sie würden auch nicht den fehlgeschlagenen Testbericht erhaltenassert multiply(3,4)
.def test_shuffle
zwei Asserts ausführt.assert multiply(*, *) == *
Sie können also eineassert_multiply
Funktion definieren . Im aktuellen Szenario spielt es keine Rolle für die Anzahl der Zeilen und die Lesbarkeit, aber bei längeren Tests können Sie komplizierte Zusicherungen, Geräte, Code zum Generieren von Geräten usw. wiederverwenden. Ich weiß nicht, ob dies eine bewährte Methode ist, aber normalerweise Dies.Nein, das ist nicht wahr.
Tests haben einen anderen Zweck als Ihre Implementierung:
quelle
Nein. Bei DRY geht es darum, Code nur einmal zu schreiben, um eine bestimmte Aufgabe auszuführen. Test ist die Bestätigung, dass die Aufgabe korrekt ausgeführt wird. Es ist ein bisschen wie ein Abstimmungsalgorithmus, bei dem die Verwendung des gleichen Codes offensichtlich nutzlos wäre.
quelle
Nein, das ultimative Ziel von DRY würde tatsächlich die Eliminierung des gesamten Produktionscodes bedeuten .
Wenn unsere Tests perfekte Spezifikationen für das System sein könnten, müssten wir nur automatisch den entsprechenden Produktionscode (oder die entsprechenden Binärdateien) generieren und so die Produktionscodebasis per se effektiv entfernen.
Dies ist tatsächlich das, was Ansätze wie die modellgetriebene Architektur zu erreichen behaupten - eine einzige vom Menschen entworfene Quelle der Wahrheit, aus der alles durch Berechnung abgeleitet wird.
Ich denke nicht, dass das Gegenteil (alle Tests loswerden) wünschenswert ist, weil:
quelle
Weil es manchmal in Ordnung ist, sich zu wiederholen. Keines dieser Prinzipien soll unter allen Umständen ohne Frage oder Kontext verstanden werden. Ich habe manchmal Tests gegen eine naive (und langsame) Version eines Algorithmus geschrieben, was eine ziemlich eindeutige Verletzung von DRY darstellt, aber definitiv von Vorteil ist.
quelle
Da es beim Unit-Test darum geht, unbeabsichtigte Änderungen zu erschweren, können manchmal auch absichtliche Änderungen erschwert werden . Diese Tatsache hängt in der Tat mit dem DRY-Prinzip zusammen.
Wenn Sie beispielsweise eine Funktion haben,
MyFunction
die im Produktionscode nur an einer Stelle aufgerufen wird, und 20 Komponententests dafür schreiben, können Sie leicht 21 Stellen in Ihrem Code haben, an denen diese Funktion aufgerufen wird. Wenn Sie nun die SignaturMyFunction
oder die Semantik oder beides ändern müssen (da sich einige Anforderungen ändern), müssen Sie 21 Stellen anstelle von nur einer ändern. Und der Grund ist in der Tat ein Verstoß gegen das DRY-Prinzip: Sie haben (mindestens) den gleichen FunktionsaufrufMyFunction
21 Mal wiederholt .Der richtige Ansatz für einen solchen Fall besteht darin, das DRY-Prinzip auch auf Ihren Testcode anzuwenden: Wenn Sie 20 Komponententests schreiben, kapseln Sie die Aufrufe
MyFunction
in Ihren Komponententests in nur wenigen Hilfsfunktionen (im Idealfall nur einer), die von der 20 Unit-Tests. Im Idealfall haben Sie nur zwei Stellen in Ihrem Code-AufrufMyFunction
: eine aus Ihrem Produktionscode und eine aus Ihren Unit-Tests. Wenn Sie also die Signatur vonMyFunction
später ändern müssen , haben Sie nur wenige Stellen, an denen Sie Ihre Tests ändern können."Ein paar Stellen" sind immer noch mehr als "ein Ort" (was Sie ohne Unit-Tests erhalten), aber die Vorteile von Unit-Tests sollten den Vorteil, weniger Code zu ändern, stark überwiegen (andernfalls führen Sie Unit-Tests vollständig durch falsch).
quelle
Eine der größten Herausforderungen beim Erstellen von Software besteht darin, die Anforderungen zu erfassen. das heißt, um die Frage zu beantworten: "Was soll diese Software tun?" Software benötigt genaue Anforderungen, um genau zu definieren, was das System tun muss. Zu denjenigen, die die Anforderungen für Softwaresysteme und -projekte definieren, gehören jedoch häufig Personen ohne Software oder formalen (mathematischen) Hintergrund. Die mangelnde Genauigkeit bei der Definition von Anforderungen zwang die Softwareentwicklung dazu, einen Weg zu finden, um Software an die Anforderungen anzupassen.
Das Entwicklungsteam stellte fest, dass die umgangssprachliche Beschreibung für ein Projekt in strengere Anforderungen übersetzt wurde. Die Testdisziplin hat sich als Kontrollpunkt für die Softwareentwicklung zusammengeschlossen, um die Lücke zwischen dem, was ein Kunde wünscht, und dem, was Software will, zu schließen. Sowohl die Softwareentwickler als auch das Qualitäts- / Testteam bilden ein Verständnis für die (informelle) Spezifikation und schreiben (unabhängig) Software oder Tests, um sicherzustellen, dass ihr Verständnis übereinstimmt. Durch Hinzufügen einer weiteren Person zum Verständnis der (ungenauen) Anforderungen wurden Fragen und eine andere Perspektive hinzugefügt, um die Genauigkeit der Anforderungen weiter zu verbessern.
Da es immer Abnahmetests gegeben hat, war es selbstverständlich, die Testrolle zu erweitern, um automatisierte Tests und Komponententests zu schreiben. Das Problem bestand darin, dass Programmierer eingestellt wurden, um Tests durchzuführen, und Sie die Perspektive von der Qualitätssicherung auf Programmierer, die Tests durchführen, einschränkten.
Trotzdem machen Sie wahrscheinlich falsche Tests, wenn sich Ihre Tests kaum von den tatsächlichen Programmen unterscheiden. Msdys Vorschlag wäre, sich mehr auf das zu konzentrieren, was in den Tests und weniger auf das Wie.
Die Ironie ist, dass die Industrie, anstatt eine formale Spezifikation der Anforderungen aus der umgangssprachlichen Beschreibung zu erfassen, Punkttests als Code zur Automatisierung des Testens implementiert hat. Anstatt formale Anforderungen zu erstellen, für deren Beantwortung Software erstellt werden könnte, bestand der Ansatz darin, einige Punkte zu testen, anstatt Software mithilfe formaler Logik zu erstellen. Dies ist ein Kompromiss, der jedoch ziemlich effektiv und relativ erfolgreich war.
quelle
Wenn Sie der Meinung sind, dass Ihr Testcode Ihrem Implementierungscode zu ähnlich ist, kann dies ein Hinweis darauf sein, dass Sie ein Mocking-Framework zu häufig verwenden. Mock-basierte Tests auf einem zu niedrigen Niveau können dazu führen, dass der Testaufbau der getesteten Methode sehr ähnlich sieht. Versuchen Sie, Tests auf höherer Ebene zu schreiben, bei denen die Wahrscheinlichkeit einer Unterbrechung geringer ist, wenn Sie Ihre Implementierung ändern (ich weiß, dass dies schwierig sein kann, aber wenn Sie es verwalten können, erhalten Sie eine nützlichere Testsuite).
quelle
Unit-Tests sollten keine Duplizierung des zu testenden Codes enthalten, wie bereits erwähnt.
Ich würde jedoch hinzufügen, dass Unit-Tests normalerweise nicht so trocken sind wie "Produktions" -Code, da das Setup in allen Tests ähnlich (aber nicht identisch) ist ... insbesondere, wenn Sie eine erhebliche Anzahl von Abhängigkeiten haben, die Sie verspotten /Fälschung.
Es ist natürlich möglich, solche Dinge in eine übliche Setup-Methode (oder eine Reihe von Setup-Methoden) umzuwandeln ... aber ich habe festgestellt, dass diese Setup-Methoden dazu neigen, lange Parameterlisten zu haben und ziemlich spröde zu sein.
Sei also pragmatisch. Wenn Sie Setup-Code konsolidieren können, ohne die Wartbarkeit zu beeinträchtigen, tun Sie dies auf jeden Fall. Wenn die Alternative jedoch ein komplexer und spröder Satz von Einrichtungsmethoden ist, ist ein wenig Wiederholung in Ihren Testmethoden in Ordnung.
Ein lokaler TDD / BDD-Evangelist drückt es so aus:
"Ihr Produktionscode sollte TROCKEN sein. Aber es ist in Ordnung, wenn Ihre Tests 'feucht' sind."
quelle
Dies ist nicht wahr, Tests beschreiben die Anwendungsfälle, während der Code einen Algorithmus beschreibt, der die Anwendungsfälle besteht, also allgemeiner. Mit TDD beginnen Sie mit dem Schreiben von Anwendungsfällen (wahrscheinlich basierend auf der User Story) und implementieren anschließend den Code, der zum Übergeben dieser Anwendungsfälle erforderlich ist. Sie schreiben also einen kleinen Test, einen kleinen Teil des Codes, und überarbeiten danach gegebenenfalls, um die Wiederholungen zu beseitigen. So funktioniert das.
Durch Tests kann es auch zu Wiederholungen kommen. Zum Beispiel können Sie Geräte, Code zum Generieren von Geräten, komplizierte Behauptungen usw. wiederverwenden. Normalerweise mache ich das, um Fehler in den Tests zu vermeiden, aber ich vergesse normalerweise, zuerst zu testen, ob ein Test wirklich fehlschlägt, und es kann den Tag wirklich ruinieren , wenn Sie eine halbe Stunde lang nach dem Fehler im Code suchen und der Test falsch ist ... xD
quelle