Ist es sinnvoll, keine Komponententests zu schreiben, da diese normalerweise später auskommentiert werden oder weil Integrationstests wertvoller sind?

35

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.

Jeff Levine
quelle
9
Sie haben es selbst gesagt: Das Schreiben von Unit-Tests führt zu besserem Code, da Sie gezwungen sind, testbaren Code zu schreiben. Unit-testbarer Code hat viele sehr wichtige Eigenschaften, die seine Gesamtqualität verbessern.
Robert Harvey
1
@Robert Harvey - Einverstanden - Ich wäre interessiert, eine Liste dieser Eigenschaften zu sehen.
Jeff Levine
18
Was den ersten Punkt betrifft - Sie sagen im Grunde, ein Nachteil von Unit-Tests ist, dass sie nicht funktionieren, wenn Sie sie nicht verwenden. Sie können das Gleiche über jede andere Praxis sagen. Auch Ihre Verwendung der Passiv-Sprache beschönigt die Tatsache, dass jemand die Unit-Tests aktiv auskommentiert. Es scheint also, dass Sie kein Buy-in von den Entwicklern im Team haben, was ein anderes Problem ist.
JacquesB

Antworten:

62

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.

Denis de Bernardy
quelle
13
Dies, das, hundertmal so. Dies ist eine großartige Sache bei der testgetriebenen Entwicklung. Wenn Sie zuerst Tests schreiben, können Sie Tests schreiben, die nicht die Details Ihrer Implementierung testen, sondern das beabsichtigte Verhalten, das Sie implementieren wollten.
Wirrbel
9
Ich denke, fragile Komponententests sind keine inhärente Eigenschaft von Komponententests, sondern verstehen nicht, wofür Komponententests gedacht sind. Was wir hier brauchen, ist eine bessere Ausbildung für Entwickler. Wenn Sie Details zur Unit-Test-Implementierung haben, machen Sie es falsch . Wenn Sie private Methoden öffentlich machen, "um sie zu testen", machen Sie es falsch . Testen Sie immer die öffentliche Schnittstelle und das Verhalten, die sich seltener als die Implementierungsdetails ändern sollten!
Andres F.
2
@ DocBrown: In der Tat nicht. Was ich damit gemeint habe, ist, dass es so oft falsch gemacht wird, dass der Kollege von OP es in den meisten Fällen richtig macht - oder zumindest in allen Fällen, in denen ich jemals in von Unit-Tests besessenen Unternehmen gestoßen bin.
Denis de Bernardy
3
@DenisdeBernardy: Mein Punkt ist: alles, was Sie geschrieben haben, ist mit viel Weisheit aus der Praxis sicherlich richtig, aber ich vermisse eine Schlussfolgerung, was dies für den Fall OPs mit seinem Kollegen im Kontext des Vorschlags von "Integrationstests als" bedeutet bessere Alternative ".
Doc Brown
5
Ich stimme Doc Brown zutiefst zu. Im Wesentlichen scheint Ihre Position zu sein, dass Unit-Tests gut sind, aber wenn sie schlecht geschrieben sind, werden sie zu einem Ärger. Daher stimmen Sie dem Kollegen von OP überhaupt nicht zu, sondern sollten lediglich sicherstellen, dass Sie Unit-Tests schreiben, bevor Sie deren Nützlichkeit geltend machen. Ich denke, Ihre Antwort ist sehr verwirrend, da der erste Satz besagt, dass Sie dem Kollegen von OP zustimmen, wenn dies nicht der Fall zu sein scheint. Es sieht eher nach einer Parole über schlecht geschriebene Komponententests aus (was in der Praxis ein Problem ist, gebe ich zu), nicht nach einem Fall gegen Komponententests im Allgemeinen.
Vincent Savard
38

Bei größeren Codeänderungen werden Komponententests eher auskommentiert als überarbeitet.

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.

Die Zeit wird besser für Integrationstests aufgewendet, die Anwendungsfälle abdecken, bei denen die Tests mit kleinerem Umfang weniger oder gar nicht wichtig sind.

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.

Doc Brown
quelle
So fühlte ich mich, als wir darüber diskutierten. Unter der Annahme, dass vollständige und relevante Integrationstests für einen Anwendungsfall geschrieben werden können, scheint es, als wäre der Komponententest ein strittiger Punkt. (Obwohl ich dies jetzt schreibe, denke ich an unerwartete zukünftige Fälle - wie zum Beispiel die Umwandlung unseres Backends in einen Webdienst für eine andere Anwendung).
Jeff Levine
+1 Für "Sie müssen an Ihrer Definition von erledigt arbeiten". Gut gesagt!
Andres F.
14

Ich weiß nicht, dass ich die Argumente Ihres Kollegen akzeptiere.

  1. Wenn Komponententests auskommentiert werden, liegt das daran, dass jemand faul ist. Das ist nicht die Schuld der Unit-Tests. Und wenn Sie anfangen, Faulheit als Ausrede zu verwenden, können Sie gegen fast jede Übung argumentieren, die ein wenig Aufwand erfordert.
  2. Wenn Sie es richtig machen, müssen Unit-Tests nicht lange dauern. Sie hindern Sie nicht daran, wichtigere Arbeiten auszuführen. Wenn wir uns alle einig sind, dass das Korrigieren von fehlerhaftem Code wichtiger ist als alles andere, ist ein Komponententest verdammt wichtig. Es ist eine einfache Möglichkeit, die Post-Bedingungen zu testen. Woher wissen Sie, dass Sie die Post-Condition-Garantien erfüllt haben? Und wenn nicht, ist Ihr Code kaputt.

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:

Scant Roger
quelle
+1 für diesen Artikel könnte eine bessere Antwort auf den OP-Fall sein als alles, was wir hier schreiben können
Doc Brown
+1 für Punkt Nummer 1 über Faulheit als Ausrede. Ich kann gar nicht genug betonen, wie frustrierend das ist. Ich war dort. In letzter Zeit eigentlich.
Greg Burghardt
3
Grund 1 ist nicht die Schuld der Unit-Tests, aber es ist immer noch ein Grund gegen Unit-Tests.
user253751
Stellen Sie nach dem Lesen des Artikels von DHH sicher, dass Sie die Videos nachschlagen, in denen er das Thema mit Kent Beck und Martin Fowler besprochen hat (sie sind auf YouTube) er verfeinert es in diesen Diskussionen ziemlich.
Jules
6

Wenn größere Codeänderungen auftreten (neue POJOs, größere Umgestaltungen von Anwendungen usw.), werden Komponententests eher auskommentiert als überarbeitet.

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
4

Unit-Tests werden eher auskommentiert als überarbeitet.

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.

Philip Kendall
quelle
3

Wenn größere Codeänderungen auftreten (neue POJOs, größere Umgestaltungen von Anwendungen usw.), werden Komponententests eher auskommentiert als überarbeitet.

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.

Die Zeit wird besser für Integrationstests verwendet, die Anwendungsfälle abdecken, wodurch die Tests mit kleinerem Umfang weniger oder gar nicht wichtig sind.

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.

Kasperd
quelle
3

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.

Michael Shaw
quelle
3

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:

  1. 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 ...

  2. 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 ...

  3. 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.

AK_
quelle
2

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.

Ewan
quelle
+1 für den Versuch, die gestellte Frage ernsthaft zu beantworten.
RubberDuck
2
Die Kosten für Unit-Tests sind definitiv nicht gering. Gute Komponententests brauchen normalerweise mehr Zeit zum Schreiben als das, was sie testen. Meistens übersteigen die Vorteile die Kosten.
AK_
1
Nur in der Theorie: Wenn Sie überarbeiten, fallen hohe Kosten für die Aktualisierung der Komponententests an, da diese fast immer viel zu detailliert sind. Wenn Sie über eine gute Integrationstestsuite sprechen, dann haben Sie Recht.
gbjbaanb
Wenn ein Projekt kurz genug ist und keine laufende Wartung / Aktualisierung erfordert - die meisten Legacy-Systeme haben als kurzes Projekt ohne Tests begonnen - und am Ende mehr Entwickler eingestellt, um das gewachsene System ohne Tests zu warten
Fabio,