Ursprünglich stammte TDD aus der agilen Bewegung, in der Tests im Voraus geschrieben wurden, um sicherzustellen, dass das, was Sie codiert haben, angesichts der Spezifikation, die jetzt im Testcode genau definiert ist, korrekt bleibt. Es schien auch ein sehr wichtiger Aspekt des Refactorings zu sein, da Sie sich beim Ändern Ihres Codes auf die Tests verlassen konnten, um zu beweisen, dass Sie das Verhalten des Codes nicht geändert hatten.
Dann kamen die Tools-Leute und dachten, sie wüssten Informationen über Ihren Code und könnten dann Teststubs generieren, die Sie beim Schreiben Ihrer Komponententests unterstützen, und ich denke, hier ist alles schief gelaufen.
Die Teststubs werden von einem Computer generiert, der keine Ahnung hat, was Sie gerade tun. Er erzeugt nur gedankenlos einen Stub für jede Methode, da er dazu aufgefordert wird. Dies bedeutet, dass Sie für jede Methode einen Testfall haben, unabhängig von der Komplexität dieser Methode oder davon, ob sie für isolierte Tests geeignet ist.
Dies geschieht beim Testen am falschen Ende der TDD-Methodik. In TDD sollten Sie herausfinden, was der Code tun soll, und dann Code erstellen, der dies erreicht. Dies ist insofern selbsterfüllend, als Sie am Ende Tests schreiben, die beweisen, dass der Code das tut, was der Code tut, und nicht das, was er tun soll. In Kombination mit der automatischen Generierung methodenbasierter Teststubs verschwenden Sie so ziemlich Ihre Zeit damit, jeden winzigen Aspekt Ihres Codes zu beweisen, der sich so leicht als falsch erweisen kann, wenn alle kleinen Teile zusammengefügt werden.
Als Fowler das Testen in seinem Buch beschrieb, bezog er sich darauf, jede Klasse mit ihrer eigenen Hauptmethode zu testen. Er hat dies verbessert, aber das Konzept ist immer noch dasselbe - Sie testen die gesamte Klasse, damit es als Ganzes funktioniert. Alle Ihre Tests werden gebündelt, um die Interaktion all dieser Methoden zu beweisen, damit die Klasse mit definierten Erwartungen wiederverwendet werden kann.
Ich denke, die Test-Toolkits haben uns einen schlechten Dienst erwiesen und uns auf den Weg gebracht zu denken, dass das Toolkit die einzige Möglichkeit ist, Dinge zu tun, wenn Sie wirklich mehr selbst überlegen müssen, um das beste Ergebnis aus Ihrem Code zu erzielen. Das blinde Einfügen von Testcode in Teststubs für winzige Teile bedeutet nur, dass Sie Ihre Arbeit ohnehin in einem Integrationstest wiederholen müssen (und wenn Sie dies tun möchten, können Sie die jetzt redundante Unit-Testphase vollständig überspringen). Dies bedeutet auch, dass die Benutzer viel Zeit damit verschwenden, eine 100% ige Testabdeckung zu erreichen, und viel Zeit damit, große Mengen an verspottetem Code und Daten zu erstellen, die besser dafür aufgewendet worden wären, den Code für Integrationstests einfacher zu machen (dh wenn Sie so viel haben Datenabhängigkeiten, Unit-Test ist möglicherweise nicht die beste Option)
Schließlich zeigt die Fragilität methodischer Unit-Tests nur das Problem. Refactoring wurde für Unit-Tests entwickelt. Wenn Ihre Tests ständig unterbrochen werden, weil Sie Refactoring durchführen, ist bei der gesamten Vorgehensweise ein schwerwiegender Fehler aufgetreten. Refactoring erstellt und löscht gerne Methoden, daher ist der auf Methoden basierende blinde Testansatz offensichtlich nicht das, was ursprünglich beabsichtigt war.
Ich habe keine Zweifel, dass für viele Methoden Tests geschrieben werden. Alle öffentlichen Methoden einer Klasse sollten getestet werden, aber Sie können nicht von dem Konzept abweichen, sie als Teil eines einzelnen Testfalls zusammen zu testen. Wenn ich zum Beispiel eine Set- und eine Get-Methode habe, kann ich Tests schreiben, die die Daten einfügen und überprüfen, ob die internen Mitglieder in Ordnung sind, oder ich kann jede verwenden, um einige Daten einzufügen und sie dann wieder herauszuholen, um zu sehen, ob sie vorhanden sind immer noch das gleiche und nicht verstümmelt. Dies testet die Klasse, nicht jede Methode für sich. Wenn der Setter auf eine private Hilfsmethode angewiesen ist, ist das in Ordnung. Sie müssen die private Methode nicht verspotten, um sicherzustellen, dass der Setter funktioniert, und nicht, wenn Sie die gesamte Klasse testen.
Ich denke, die Religion befasst sich mit diesem Thema, daher sehen Sie das Schisma in der heutigen "verhaltensgesteuerten" und "testgesteuerten" Entwicklung - das ursprüngliche Konzept des Unit-Tests war für die verhaltensgesteuerte Entwicklung.
Wie der Name schon sagt, testen Sie in jedem Test ein atomares Subjekt. Ein solches Thema ist normalerweise eine einzelne Methode. Mehrere Tests können dieselbe Methode testen, um den glücklichen Pfad, mögliche Fehler usw. abzudecken. Sie testen das Verhalten, nicht die interne Mechanik. Beim Unit-Test geht es also wirklich darum, die öffentliche Schnittstelle einer Klasse zu testen, dh eine bestimmte Methode.
Beim Unit-Test muss eine Methode isoliert getestet werden, dh durch Stubben / Verspotten / Fälschen von Abhängigkeiten. Andernfalls wird das Testen einer Einheit mit "echten" Abhängigkeiten zu einem Integrationstest. Für beide Arten von Tests gibt es Zeit und Ort. Unit-Tests stellen sicher, dass ein einzelnes Thema wie erwartet eigenständig funktioniert. Integrationstests stellen sicher, dass „echte“ Probanden korrekt zusammenarbeiten.
quelle
Meine Faustregel: Die kleinste Codeeinheit, die immer noch komplex genug ist, um Fehler zu enthalten.
Ob dies eine Methode, eine Klasse oder ein Subsystem ist, hängt vom jeweiligen Code ab. Es kann keine allgemeine Regel angegeben werden.
Beispielsweise bietet es keinen Wert zum einfachen Testen einfacher Getter / Setter-Methoden oder Wrapper-Methoden, die nur eine andere Methode aufrufen. Sogar eine ganze Klasse ist möglicherweise zu einfach zu testen, wenn die Klasse nur ein dünner Wrapper oder Adapter ist. Wenn Sie nur testen müssen, ob eine Methode für ein Modell aufgerufen wird, ist der zu testende Code zu dünn.
In anderen Fällen kann eine einzelne Methode eine komplexe Berechnung durchführen, die sich für einen isolierten Test lohnt.
In vielen Fällen handelt es sich bei den komplexen Teilen nicht um einzelne Klassen, sondern um die Integration zwischen Klassen. Sie testen also zwei oder mehr Klassen gleichzeitig. Einige werden sagen, dass dies keine Komponententests, sondern Integrationstests sind, aber die Terminologie spielt keine Rolle: Sie sollten testen, wo die Komplexität liegt, und diese Tests sollten Teil der Testsuite sein.
quelle