Ich habe mit einem Kollegen über Unit- / Integrationstests gesprochen, und er hat ein interessantes Argument gegen das Schreiben von Unit-Tests angeführt. Ich bin ein großer Unit-Test-Befürworter (in erster Linie JUnit), bin aber daran interessiert, die Einstellungen anderer zu hören, da er einige interessante Punkte hervorgehoben hat.
Um seine Punkte zusammenzufassen:
- Wenn größere Codeänderungen auftreten (neue POJOs, größere Umgestaltungen von Anwendungen usw.), werden Komponententests eher auskommentiert als überarbeitet.
- Die Zeit wird besser für Integrationstests verwendet, die Anwendungsfälle abdecken, wodurch die Tests mit kleinerem Umfang weniger oder gar nicht wichtig sind.
Gedanken dazu? Ich bin immer noch Pro-Unit-Test (wie ich durchweg sehe, verbesserter Code), obwohl Integrationstests mindestens genauso wertvoll klingen.
unit-testing
integration-tests
Jeff Levine
quelle
quelle
Antworten:
Ich tendiere dazu, auf der Seite Ihres Freundes zu stehen, weil Unit-Tests allzu oft die falschen Dinge testen .
Unit-Tests sind von Natur aus nicht schlecht. Sie testen jedoch häufig die Implementierungsdetails und nicht den Eingabe- / Ausgabefluss. In diesem Fall haben Sie völlig sinnlose Tests. Meine eigene Regel ist, dass ein guter Komponententest Ihnen sagt, dass Sie gerade etwas kaputt gemacht haben. Ein schlechter Unit-Test zeigt Ihnen lediglich, dass Sie gerade etwas geändert haben.
Ein Beispiel aus der Luft ist ein Test, der vor ein paar Jahren in WordPress steckte. Die getestete Funktionalität drehte sich um Filter, die sich gegenseitig aufriefen, und die Tests stellten sicher, dass Rückrufe dann in der richtigen Reihenfolge aufgerufen wurden. Anstatt (a) die Kette zu durchlaufen, um sicherzustellen, dass Rückrufe in der erwarteten Reihenfolge aufgerufen werden, konzentrierten sich die Tests auf (b) das Lesen eines internen Zustands, der anfangs vermutlich nicht hätte offen gelegt werden dürfen. Wechseln Sie die Einbauten und (b) wird rot; wohingegen (a) nur rot wird, wenn Änderungen an den Interna dabei das erwartete Ergebnis stören. (b) war aus meiner Sicht eindeutig ein sinnloser Test.
Wenn Sie eine Klasse, die ein paar Methoden zur Außenwelt sind die letztgenannten Methoden, die richtige Sache zu Test in meiner Sicht macht nur . Wenn Sie auch die interne Logik testen, kann es sein, dass Sie die interne Logik der Außenwelt mit verschlungenen Testmethoden oder mit einer Litanei von Komponententests aussetzen, die immer dann unterbrochen werden, wenn Sie etwas ändern möchten.
Nach alledem wäre ich überrascht, wenn Ihr Freund Unit-Tests an sich genauso kritisch gegenübersteht, wie Sie es vermuten. Eher würde ich sagen, dass er pragmatisch ist. Das heißt, er stellte fest, dass die Unit-Tests, die geschrieben werden, in der Praxis meist sinnlos sind. Zitat: "Unit-Tests werden eher auskommentiert als überarbeitet". Für mich ist da eine implizite Botschaft drin - wenn sie dazu neigen, überarbeitet zu werden, dann deshalb, weil sie dazu neigen, zu saugen. Angenommen, der zweite Satz lautet: Entwickler würden weniger Zeit damit verschwenden, Code zu schreiben, der schwerer zu verwechseln ist - dh Integrationstests.
Als solches geht es nicht darum, ob man besser oder schlechter ist. Es ist nur so, dass man sich viel leichter irren kann, und zwar sehr oft in der Praxis.
quelle
Mit einer undisziplinierten Gruppe von Cowboy-Programmierern, die denken, dass alle Tests "grün" werden, wenn Sie alle vorhandenen Tests auskommentieren: natürlich. Die Überarbeitung von Unit-Tests erfordert die gleiche Disziplin wie das Schreiben aus erster Hand. Sie müssen also hier an der "Definition von erledigt" Ihres Teams arbeiten.
Das ist weder ganz falsch noch ganz richtig. Der Aufwand für das Schreiben nützlicher Integrationstests hängt stark von der Art der Software ab, die Sie schreiben. Wenn Sie eine Software haben, mit der Integrationstests fast genauso einfach geschrieben werden können wie Komponententests, die dennoch schnell ausgeführt werden und Ihnen eine gute Abdeckung Ihres Codes bieten, dann hat Ihr Kollege Recht. Aber für viele Systeme, die ich gesehen habe, können diese drei Punkte nicht einfach durch Integrationstests erfüllt werden. Deshalb sollten Sie auch Unit-Tests anstreben.
quelle
Ich weiß nicht, dass ich die Argumente Ihres Kollegen akzeptiere.
Es gibt andere, überzeugendere Argumente gegen Unit-Tests. Na ja, nicht gerade gegen sie. Beginnen Sie mit DHHs Test-induziertem Designschaden . Er ist ein lustiger Schriftsteller, also lies es. Was ich daraus genommen habe:
Wenn Sie umfangreiche Konstruktionsänderungen vornehmen, um Ihre Komponententests durchzuführen, können diese andere Ziele in Ihrem Projekt beeinträchtigen. Fragen Sie nach Möglichkeit eine unparteiische Person, welche Vorgehensweise leichter zu befolgen ist. Wenn Ihr gut testbarer Code schwer zu verstehen ist, verlieren Sie möglicherweise alle Vorteile, die Sie durch Unit-Tests erhalten haben. Der kognitive Aufwand von zu viel Abstraktion bremst Sie und macht undichte Fehler leichter. Diskontiere es nicht.
Wenn Sie nuanciert und philosophisch werden möchten, gibt es viele großartige Ressourcen:
quelle
Mach das nicht. Wenn Sie jemanden finden, der das tut, töten Sie ihn und stellen Sie sicher, dass er sich nicht vermehrt, da seine Kinder dieses defekte Gen erben könnten. Der Schlüssel, wie ich ihn beim Anschauen von CSI-Fernsehsendungen sehe, ist, überall eine Menge irreführender DNA-Proben zu verstreuen. Auf diese Weise verschmutzen Sie den Tatort mit so viel Lärm, dass die Wahrscheinlichkeit, dass er Sie trifft, angesichts der kostspieligen und zeitaufwändigen DNA-Tests astronomisch gering ist.
quelle
An diesem Punkt wird im Code-Überprüfungsprozess "go fix the unit tests" angezeigt und dies ist kein Problem mehr :-) Wenn Ihr Code-Überprüfungsprozess diese Art von schwerwiegendem Fehler im eingereichten Code nicht feststellt, ist dies das Problem.
quelle
Ich versuche immer, Refactoring und Funktionsänderung getrennt zu halten. Wenn ich beides tun muss, führe ich normalerweise zuerst das Refactoring durch.
Beim Refactoring von Code ohne Änderung der Funktionalität sollen vorhandene Komponententests sicherstellen, dass das Refactoring die Funktionalität nicht versehentlich beeinträchtigt. Daher würde ich bei einem solchen Commit das Deaktivieren oder Entfernen von Komponententests als wichtiges Warnzeichen betrachten. Entwickler, die dies tun, sollten angewiesen werden, dies nicht zu tun, wenn der Code überprüft wird.
Änderungen, die die Funktionalität nicht ändern, können dazu führen, dass Komponententests aufgrund fehlerhafter Komponententests fehlschlagen. Wenn Sie den Code verstehen, den Sie ändern, ist die Ursache für solche Unit-Test-Fehler normalerweise sofort offensichtlich und leicht zu beheben.
Wenn eine Funktion beispielsweise drei Argumente enthält, hat ein Komponententest, der die Interaktion zwischen den ersten beiden Argumenten für die Funktion abdeckt, möglicherweise nicht dafür gesorgt, dass für das dritte Argument ein gültiger Wert angegeben wurde. Dieser Fehler im Komponententest kann durch ein Refactoring des getesteten Codes aufgedeckt werden, ist jedoch leicht zu beheben, wenn Sie verstehen, was der Code tun soll und was der Komponententest testet.
Wenn Sie vorhandene Funktionen ändern, müssen Sie in der Regel auch einige Komponententests ändern. In diesem Fall stellen Unit-Tests sicher, dass Ihr Code die Funktionalität wie beabsichtigt ändert und keine unbeabsichtigten Nebenwirkungen hat.
Wenn Sie Fehler beheben oder neue Funktionen hinzufügen, müssen Sie in der Regel weitere Komponententests hinzufügen. Für diese kann es hilfreich sein, zuerst Komponententests durchzuführen und die Fehlerbehebung oder neue Funktionen später durchzuführen. Dies erleichtert die Überprüfung, dass die neuen Komponententests nicht mit dem älteren Code, sondern mit dem neueren Code bestanden wurden. Dieser Ansatz ist jedoch nicht ganz ohne Nachteile. Daher gibt es auch Argumente dafür, sowohl neue Komponententests als auch Code-Updates gleichzeitig durchzuführen.
Darin liegt ein Element der Wahrheit. Wenn Sie die unteren Schichten des Software-Stacks mit Tests abdecken können, die auf die höheren Schichten des Software-Stacks abzielen, sind Ihre Tests möglicherweise beim Refactoring von Code hilfreicher.
Ich glaube nicht, dass Sie eine Einigung über die genaue Unterscheidung zwischen einem Unit-Test und einem Integrationstest finden werden. Und ich würde mir keine Sorgen machen, wenn Sie einen Testfall haben, den ein Entwickler einen Komponententest und ein anderer einen Integrationstest nennt, vorausgesetzt, sie stimmen darin überein, dass es sich um einen nützlichen Testfall handelt.
quelle
Durch Integrationstests wird geprüft, ob sich das System so verhält, wie es sein soll, was immer einen geschäftlichen Nutzen hat. Unit-Tests machen das nicht immer. Unit-Tests können beispielsweise das Testen von totem Code sein.
Es gibt eine Testphilosophie, die besagt, dass jeder Test, der Ihnen keine Informationen liefert, eine Verschwendung ist. Wenn ein Teil des Codes nicht bearbeitet wird, sind seine Komponententests immer (oder fast immer) grün und geben Ihnen nur wenige oder gar keine Informationen.
Jede Testmethode ist unvollständig. Selbst wenn Sie eine 100% ige Abdeckung durch eine Metrik erhalten, durchlaufen Sie immer noch nicht nahezu jeden möglichen Satz von Eingabedaten. Denken Sie also nicht, dass Unit-Tests magisch sind.
Ich habe oft die Behauptung gesehen, dass Unit-Tests Ihren Code verbessern. Meiner Meinung nach zwingen Unit-Tests Leute, die nicht daran gewöhnt sind, ihren Code in kleine, gut faktorisierte Teile zu zerlegen. Wenn Sie sich normalerweise daran gewöhnt haben, Ihren Code in kleinen, gut faktorisierten Teilen zu entwerfen, werden Sie möglicherweise feststellen, dass Sie keine Komponententests mehr benötigen, um dies zu erzwingen.
Abhängig davon, wie umfangreich Ihr Unit-Test ist und wie umfangreich Ihr Programm ist, werden Sie möglicherweise eine enorme Anzahl von Unit-Tests erhalten. Wenn ja, werden große Veränderungen schwieriger. Wenn eine große Änderung viele fehlgeschlagene Komponententests verursacht, können Sie die Änderung ablehnen, viel Arbeit zur Behebung vieler Tests leisten oder die Tests wegwerfen. Keine der Möglichkeiten ist ideal.
Unit-Tests sind ein Werkzeug in der Toolbox. Sie sind da, um Ihnen zu dienen, nicht umgekehrt. Stellen Sie sicher, dass Sie mindestens so viel aus ihnen herausholen, wie Sie hineingesteckt haben.
quelle
Unit-Test ist definitiv nicht die Silberkugel, die einige Befürworter für sich beanspruchen. Im Allgemeinen haben sie jedoch ein sehr positives Kosten-Nutzen-Verhältnis. Sie werden Ihren Code nicht "besser" machen, vielleicht modularer. "Besser" hat so viel mehr Faktoren als "Testbarkeit" impliziert. Aber sie werden sicherstellen, dass es in allen Fällen und Anwendungen funktioniert, über die Sie nachgedacht haben, und die meisten Ihrer Fehler beseitigen (wahrscheinlich die trivialeren).
Der größte Vorteil von Komponententests ist jedoch, dass sie Ihren Code flexibler und unempfindlicher gegen Änderungen machen. Sie können Features umgestalten oder hinzufügen und sich ziemlich sicher sein, dass Sie nichts kaputt gemacht haben. Dies allein macht sie für die meisten Projekte lohnenswert.
Bezugnehmend auf die Punkte, die Ihr Freund gemacht hat:
Wenn das Hinzufügen von POJOs Ihre Komponententests bricht, sind sie wahrscheinlich sehr schlecht geschrieben. POJOs sind in der Regel die Sprache, in der Sie über Ihre Domain sprechen, und die Probleme, die Sie lösen, wenn Sie sie ändern, bedeuten offensichtlich, dass sich Ihre gesamte Geschäftslogik und wahrscheinlich der Präsentationscode ändern muss. Sie können nicht erwarten, was sicherstellt, dass sich diese Teile Ihres Programms nicht ändern ...
Sich darüber zu beschweren, dass Unit Tests schlecht sind, weil die Leute sie auskommentieren, ist wie sich zu beschweren, dass Sicherheitsgurte schlecht sind, weil die Leute sie nicht tragen. Wenn man einen Testcode auskommentiert und etwas kaputt macht, sollte er in ein sehr ernstes und unangenehmes Gespräch mit seinem Chef geraten ...
Integrationstests sind Unit-Tests weit überlegen. keine Frage. vor allem, wenn Sie ein Produkt testen. Es ist jedoch unglaublich schwierig, gute, umfassende Integrationstests zu schreiben, mit denen Sie den gleichen Wert, die gleiche Abdeckung und die gleiche Korrektheit wie mit einem Komponententest erzielen.
quelle
Ich kann mir nur ein Argument gegen Unit-Tests vorstellen, und es ist ziemlich schwach.
Unit-Tests zeigen den größten Teil ihres Wertes, wenn Sie den Code zu einem späteren Zeitpunkt überarbeiten oder ändern. Sie sehen eine enorme Zeitersparnis beim Hinzufügen von Funktionen und stellen sicher, dass Sie nichts kaputt gemacht haben.
Allerdings: Der Zeitaufwand für das Schreiben von Tests ist in erster Linie (gering).
Deshalb: Wenn ein Projekt kurz genug ist und keine laufende Wartung / Aktualisierung erfordert, überwiegen möglicherweise die Kosten für das Schreiben von Tests die Vorteile.
quelle