Riechen zu viele Behauptungen nach Code?

19

Ich habe mich wirklich in Unit-Tests und TDD verliebt - ich bin testinfiziert.

Unit-Tests werden jedoch normalerweise für öffentliche Methoden verwendet. Manchmal muss ich allerdings einige Annahmen-Behauptungen auch in privaten Methoden testen, da einige von ihnen "gefährlich" sind und Refactoring nicht weiterhelfen kann. (Ich weiß, dass das Testen von Frameworks das Testen privater Methoden ermöglicht.)

So wurde es zu meiner Gewohnheit, dass sowohl die erste als auch die letzte Zeile einer privaten Methode Behauptungen sind.

Mir ist jedoch aufgefallen, dass ich Behauptungen eher in öffentlichen Methoden (als auch im privaten) verwende, nur "um sicherzugehen". Könnte dies "Testen von Duplikaten" sein, da die Annahmen der öffentlichen Methode von außen durch das Unit-Testing-Framework getestet werden?

Könnte sich jemand zu viele Behauptungen als Codegeruch vorstellen?

Florents Tselai
quelle
1
Sind diese Behauptungen in der Veröffentlichung aktiviert oder deaktiviert?
jk.
Ich arbeite hauptsächlich mit Java, wobei Zusicherungen manuell aktiviert werden müssen.
Florents Tselai
Ich habe gerade festgestellt, dass "Behauptungen die Produktivität steigern, indem sie Fehler aufspüren" programmers.stackexchange.com/a/16317/32342
Florents Tselai
Wie definieren Sie "zu viele"?
Haylem
@haylem Es gibt "zu viele" Behauptungen, wenn ein anderer Programmierer den Code liest und sagt "Ich habe diese Behauptungen satt".
Florents Tselai

Antworten:

26

Diese Behauptungen sind sehr nützlich, um Ihre Annahmen zu testen, aber sie dienen auch einem anderen wirklich wichtigen Zweck: der Dokumentation. Jeder Leser einer öffentlichen Methode kann die Asserts lesen, um die Vor- und Nachbedingungen schnell zu ermitteln, ohne sich die Testsuite ansehen zu müssen. Aus diesem Grund empfehle ich, diese Zusicherungen aus Dokumentationsgründen und nicht aus Testgründen aufzubewahren. Technisch gesehen duplizieren Sie die Behauptungen, aber sie dienen zwei unterschiedlichen Zwecken und sind in beiden Fällen sehr nützlich.

Es ist besser, sie als Zusicherungen beizubehalten, als nur Kommentare zu verwenden, da sie die Annahmen bei jeder Ausführung aktiv überprüfen.

Oleksi
quelle
2
Sehr guter Punkt!
Florents Tselai
1
Behauptungen sind Dokumentation, aber das macht eine Vervielfältigung nicht legitim. Im Gegenteil, es werden sogar Vermutungen über die Qualität der Tests laut, wenn es den Anschein hat, dass dieselben Behauptungen mehr als einmal benötigt werden.
Yam Marcovic
1
@YamMarcovic Ich halte dies für akzeptabel, da die beiden "duplizierten" Codeteile zwei unterschiedlichen und unterschiedlichen Zwecken dienen. Ich denke, es ist nützlich, sie an beiden Standorten zu haben, trotz der Duplizierung. Die Behauptungen in der Methode zu haben, ist für die Dokumentation von unschätzbarem Wert, und sie werden offensichtlich zum Testen benötigt.
Oleksi
2
Code-Aussagen ergänzen für mich die Unit-Testing-Aussagen. Sie werden keine 100% ige Testabdeckung haben und die Behauptungen im Code helfen Ihnen, fehlgeschlagene Komponententests schneller einzugrenzen.
Sebastiangeiger
15

Es hört sich so an, als würden Sie versuchen, Design-by-Contract von Hand zu erstellen .

DbC ist eine gute Idee, aber Sie sollten zumindest in Betracht ziehen, zu einer Sprache zu wechseln, die es von Haus aus unterstützt (wie Eiffel ), oder zumindest ein Vertragsframework für Ihre Plattform verwenden (z. B. Microsoft Code Contracts für .NET)ist ziemlich nett, der wohl ausgefeilteste Vertragsrahmen überhaupt, sogar mächtiger als Eiffel. Auf diese Weise können Sie die Leistungsfähigkeit von Verträgen besser nutzen. Sie können z. B. mit einem vorhandenen Framework die Verträge in Ihrer Dokumentation oder einer IDE anzeigen (z. B. werden Codeverträge für .NET in IntelliSense angezeigt und können, wenn Sie VS Ultimate haben, während der Kompilierung sogar von einem automatischen Theorembeweiser statisch überprüft werden Sie tippen, und viele Java-Vertrags-Frameworks haben JavaDoc-Doclets, die die Verträge automatisch in Ihre JavaDoc-API-Dokumentation extrahieren.

Und selbst wenn sich herausstellt, dass es in Ihrer Situation keine Alternative zu einer manuellen Ausführung gibt, wissen Sie jetzt zumindest, wie es heißt, und können sich darüber informieren.

Also, kurz gesagt: Wenn Sie tatsächlich DbC machen, auch wenn Sie es nicht wissen, dann sind diese Behauptungen vollkommen in Ordnung.

Jörg W. Mittag
quelle
4

Wann immer Sie nicht die volle Kontrolle über Ihre Eingabeparameter haben, ist es eine sehr gute Idee, vorab auf einfache Fehler zu testen. Zum Beispiel auf null fehlschlagen.

Dies ist nicht Vervielfältigung Ihrer Tests, da sie , dass der Code versagt angemessen testen sollte gegeben schlecht Eingabeparameter und dann dokumentieren , dass .

Ich denke nicht, dass Sie auf den Rückgabeparametern behaupten sollten (es sei denn, Sie haben explizit eine Invariante, die der Leser verstehen soll). Dies ist auch die Aufgabe der Unittests.

Persönlich mag ich die assertAussage in Java nicht, da sie abgeschaltet werden kann und es sich dann um eine falsche Sicherheit handelt.


quelle
1
+1 Für "Ich mag die assert-Anweisung in Java nicht, da sie deaktiviert werden kann und es sich dann um eine falsche Sicherheit handelt."
Florents Tselai
3

Ich denke, dass die Verwendung von Behauptungen in öffentlichen Methoden noch wichtiger ist, da Sie dort die Eingaben nicht kontrollieren und wahrscheinlich eine fehlerhafte Annahme haben.

Das Testen der Eingabebedingungen sollte mit allen öffentlichen und geschützten Methoden erfolgen. Wenn die Eingaben direkt an eine private Methode übergeben werden, ist das Testen der Eingaben möglicherweise redundant.

Das Testen von Ausgabebedingungen (z. B. Objektstatus oder Rückgabewert! = Null) sollte in den inneren Methoden (z. B. privaten Methoden) erfolgen. Wenn die Ausgaben ohne zusätzliche Berechnungen oder Änderungen des inneren Zustands direkt von einer privaten Methode an die Ausgabe einer öffentlichen Methode übergeben werden, ist das Testen der Ausgabebedingungen der öffentlichen Methode möglicherweise redundant.

Ich stimme jedoch Oleksi zu, dass die Redundanz die Lesbarkeit und die Wartbarkeit erhöhen kann (wenn die direkte Zuweisung oder Rückgabe in Zukunft nicht mehr der Fall ist).

Danny Varod
quelle
4
Wenn vom Benutzer (Aufrufer) der öffentlichen Schnittstelle eine Fehlerbedingung oder eine ungültige Argumentbedingung verursacht werden kann, sollte diese vollständig ordnungsgemäß behandelt werden. Dies bedeutet, dass eine Fehlermeldung formatiert wird, die für den Endbenutzer von Bedeutung ist, zusammen mit einem Fehlercode, einer Protokollierung und dem Versuch, Daten wiederherzustellen oder einen vernünftigen Zustand wiederherzustellen. Durch Ersetzen der ordnungsgemäßen Fehlerbehandlung durch Zusicherungen wird der Code weniger robust und die Fehlerbehebung schwieriger.
Rwong
Die Zusicherungen können (und sollten in vielen Fällen) das Protokollieren und Auslösen von Ausnahmen (oder Fehlercodes in C--) umfassen.
Danny Varod
3

Es ist schwierig, sprachunabhängig in Bezug auf dieses Problem zu sein, da die Details der Implementierung von Behauptungen und der ordnungsgemäßen Fehler- / Ausnahmebehandlung einen Einfluss auf die Antwort haben. Hier sind meine 0,02 US-Dollar, basierend auf meinen Kenntnissen in Java und C ++.

Behauptungen in privaten Methoden sind eine gute Sache, vorausgesetzt, Sie gehen nicht über Bord und platzieren sie überall . Wenn Sie sie mit wirklich einfachen Methoden anwenden oder wiederholt Dinge wie unveränderliche Felder überprüfen, ist der Code unnötig überfüllt.

Behauptungen in öffentlichen Methoden werden im Allgemeinen am besten vermieden. Sie sollten weiterhin überprüfen, ob der Methodenvertrag nicht verletzt wurde. Wenn dies jedoch der Fall ist, sollten Sie entsprechend typisierte Ausnahmebedingungen mit aussagekräftigen Nachrichten auslösen, den Status nach Möglichkeit wiederherstellen usw. (was @rwong als "vollständig" bezeichnet) ordnungsgemäße Fehlerbehandlung ").

Im Allgemeinen sollten Sie Behauptungen nur verwenden, um Ihre Entwicklung / Ihr Debugging zu unterstützen. Sie können nicht davon ausgehen, dass jeder, der Ihre API verwendet, sogar Zusicherungen aktiviert hat, wenn er Ihren Code verwendet. Obwohl sie bei der Dokumentation des Codes hilfreich sind, gibt es oft bessere Möglichkeiten, um dieselben Dinge zu dokumentieren (z. B. Methodendokumentation, Ausnahmen).

vaughandroid
quelle
2

Ein weiterer Grund für viele Behauptungen und Duplikationen ist, dass Sie nicht wissen, wie, wann oder von wem die Klasse im Laufe ihrer Lebensdauer geändert wird. Wenn Sie Code zum Wegwerfen schreiben, der in einem Jahr tot sein wird, ist dies kein Problem. Wenn Sie Code schreiben, der in mehr als 20 Jahren verwendet wird, sieht er ganz anders aus - und eine Annahme, die Sie getroffen haben, ist möglicherweise nicht mehr gültig. Der Typ, der diese Änderung vornimmt, wird Ihnen für die Behauptungen danken.

Auch sind nicht alle Programmierer perfekt - auch hier bedeuten die Behauptungen, dass sich einer dieser "Ausrutscher" nicht zu weit ausbreitet.

Unterschätzen Sie nicht die Auswirkung dieser Behauptungen (und anderer Überprüfungen von Annahmen) auf die Reduzierung der Wartungskosten.

mattnz
quelle
0

Doppelte Zusicherungen sind per se nicht falsch, aber die Zusicherungen "nur um sicherzustellen" sind nicht die besten. Es läuft wirklich darauf hinaus, was genau jeder Test zu erreichen versucht. Nur behaupten, was unbedingt benötigt wird. Wenn ein Test mithilfe von Moq überprüft, ob eine Methode aufgerufen wurde, spielt es keine Rolle, was nach dem Aufrufen dieser Methode geschieht. Der Test muss lediglich sicherstellen, dass die Methode aufgerufen wurde.

Wenn jeder einzelne Komponententest mit Ausnahme von einem oder zwei denselben Satz von Zusicherungen enthält, können alle Tests, wenn Sie ein wenig umgestalten, aus genau demselben Grund fehlschlagen, anstatt Ihnen die tatsächlichen Auswirkungen des Umgestalters zu zeigen. Sie könnten in eine Situation geraten, in der Sie alle Tests ausführen, die alle fehlschlagen, weil jeder Test die gleichen Aussagen enthält. Sie beheben diesen Fehler, führen die Tests erneut aus, sie schlagen aus einem anderen Grund fehl, und Sie beheben diesen Fehler. Wiederholen, bis fertig.

Berücksichtigen Sie auch die Wartung Ihres Testprojekts. Was passiert, wenn Sie einen neuen Parameter hinzufügen oder die Ausgabe geringfügig optimieren? Müssen Sie eine Reihe von Tests wiederholen und eine Zusicherung ändern oder eine Zusicherung hinzufügen? Abhängig von Ihrem Code müssen Sie möglicherweise viel mehr Einstellungen vornehmen, um sicherzustellen, dass alle Asserts erfolgreich sind.

Ich kann den Reiz verstehen, doppelte Aussagen in den Komponententest einbeziehen zu wollen, aber es ist wirklich übertrieben. Bei meinem ersten Testprojekt bin ich den gleichen Weg gegangen. Ich bin davon weggegangen, weil mich die Wartung allein verrückt gemacht hat.

bwalk2895
quelle
0

Unit-Tests werden jedoch für öffentliche Methoden verwendet.

Wenn Ihr Test-Framework Zugriffsmethoden zulässt, ist das Testen privater Methoden in Units ebenfalls eine gute Idee (IMO).

Könnte sich jemand zu viele Behauptungen als Codegeruch vorstellen?

Ich mache. Behauptungen sind in Ordnung, aber Ihr erstes Ziel sollte es sein, dass das Szenario nicht einmal möglich ist . nicht nur, dass es nicht passiert.

Telastyn
quelle
-2

Es ist eine schlechte Übung.

Sie sollten keine öffentlichen / privaten Methoden testen. Sie sollten die Klasse als Ganzes testen. Stellen Sie einfach sicher, dass Sie eine gute Abdeckung haben.

Wenn Sie die (primitive) Methode als Faustregel verwenden, um jede Methode einzeln zu testen, fällt es Ihnen extrem schwer, Ihre Klasse in Zukunft neu zu faktorisieren. Und das ist eines der besten Dinge, die TDD Ihnen ermöglicht.

Außerdem ist es eine Vervielfältigung. Darüber hinaus deutet dies darauf hin, dass der Verfasser des Codes nicht wirklich wusste, was er tat. Und das Lustige ist, dass du es tust .

Einige Leute behandeln ihre Tests mit weniger Sorgfalt als ihren Produktionscode. Sie sollten nicht. Meiner Erfahrung nach ist es sogar ratsam, die Tests mit größerer Sorgfalt zu behandeln. Sie sind es wert.

Yam Marcovic
quelle
-1 Das Testen einer Klasse als Ganzes kann gefährlich sein, da Sie am Ende mehr als eine Sache pro Test testen. Dies macht wirklich spröde Tests. Sie sollten ein Verhalten auf einmal testen, was häufig nur einer Methode pro Test entspricht.
Oleksi
Auch in Bezug auf "es deutet darauf hin, dass der Schreiber des Codes nicht wirklich wusste, was er tat, [...] was Sie tun." Zukünftige Entwickler wissen möglicherweise nicht so genau, was eine bestimmte Methode leistet. In diesem Fall kann das Vorhandensein von Vor- und Nachbedingungen in Form von Zusicherungen für das Verständnis eines Codeteils von unschätzbarem Wert sein.
Oleksi
2
@Oleksi Bei guten Tests geht es um gültige Verwendungsszenarien, nicht darum, jedes kleinste irrelevante Detail herauszustellen. Redundante Behauptungen sind in der Tat ein Codegeruch, weil die Absicht unklar ist, und sind nur ein weiteres schlechtes Beispiel für defensive Programmierung.
Yam Marcovic