Was wird unter "Einheit" in Einheitentests verstanden?


9

Wie ich theoretisch unter "Einheit" verstehe, meinen Menschen Methode (in OOP). In der Praxis sind Tests, die eine Methode isoliert verifizieren, jedoch sehr fragile Verhaltenstests (die nicht das Ergebnis, sondern die Tatsache überprüfen, dass eine Abhängigkeitsmethode aufgerufen wurde). Ich sehe also viele Leute, die nach Einheiten eine kleine Gruppe eng verwandter Klassen verstehen. In diesem Fall werden nur äußere Abhängigkeiten verspottet / gestoppt und für Abhängigkeiten, die sich innerhalb der Einheit befinden, werden reale Implementierungen verwendet. In diesem Fall gibt es mehr staatliche, aussagekräftige (gemäß Spezifikation) und nicht so fragile Tests. Die Frage ist also, wie Sie sich zu diesen Ansätzen fühlen. Ist es gültig, den zweiten Ansatz als Unit-Test zu bezeichnen, oder handelt es sich möglicherweise um eine Art Low-Level-Integrationstest?

Wenn Sie einige spezifische Überlegungen zur Anwendung von TDD durch eine dieser Testmethoden sehen, wäre ich Ihnen für Ihre Gedanken dankbar.

Antworten:


11

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.


10

Eine Einheit wird am häufigsten als " kleinster testbarer Teil einer Anwendung " definiert. Meistens bedeutet dies eine Methode. Und ja, dies bedeutet, dass Sie nicht das Ergebnis einer abhängigen Methode testen sollten, sondern nur, dass die Methode aufgerufen wird (und dann nur einmal, wenn möglich, nicht in jedem Test für diese Methode).

Du nennst das zerbrechlich. Ich denke das ist falsch. Fragile Tests sind solche, die bei der geringsten Änderung an nicht verwandtem Code brechen. Das heißt, diejenigen, die auf Code angewiesen sind, der für die getestete Funktionalität irrelevant ist.

Ich denke jedoch, dass Sie wirklich sagen wollen, dass das Testen einer Methode ohne ihre Abhängigkeiten nicht gründlich ist. In diesem Punkt würde ich zustimmen. Sie benötigen auch Integrationstests, um sicherzustellen, dass die Codeeinheiten korrekt miteinander verbunden sind, um eine Anwendung zu erstellen.

Dies ist genau das Problem, das die verhaltensgesteuerte Entwicklung und insbesondere die akzeptanztestgesteuerte Entwicklung lösen soll. Damit entfällt jedoch nicht die Notwendigkeit von Unit-Tests / testgetriebener Entwicklung. es ergänzt es lediglich.


Ich wollte wirklich sagen, dass Verhaltenstests fragil sind. Sie werden beim Ändern der Codebasis häufig falsch negativ. Es passiert weniger mit Zustandstests (aber Zustandstests sind für Unit-Tests sehr selten)
SiberianGuy

@ Idsa: Ich bin ein wenig verloren von Ihren Definitionen. Verhaltenstests sind Integrationstests, bei denen ein bestimmtes Verhalten wie angegeben getestet wird. Wenn Sie Ihre ursprüngliche Frage lesen, scheint es, dass Sie dasselbe meinen, wenn Sie Zustandstests sagen.
pdr

mit Zustand meine ich Test, der den Zustand verifiziert, Ergebnis einer Funktion; Mit Verhalten meine ich Test, der nicht das Ergebnis bestätigt, sondern die Tatsache, dass eine Funktion aufgerufen wurde
SiberianGuy

@ Idsa: In diesem Fall bin ich völlig anderer Meinung. Was Sie als Zustandstest bezeichnen, nenne ich Integration. Was du Verhalten nennst, nenne ich Einheit. Integrationstests sind per Definition fragiler. Google "Integrationstesteinheit zerbrechlich" und Sie werden sehen, dass ich nicht allein bin.
pdr

Es gibt ein Protokoll mit Artikeln zum Testen, aber welche davon teilen Ihre Meinung?
SiberianGuy

2

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.


1
Nicht ganz, eine Einheit ist ein isoliertes Thema, nur weil die automatisierten Werkzeuge es vorziehen, eine Methode zu behandeln, da dies sie weder so macht noch zum besten macht. "Isoliert" ist hier der Schlüssel. Selbst wenn Sie Methoden testen, sollten Sie auch die privaten testen.
Gbjbaanb

1

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.

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