Sollen wir Tests für unsere Getter und Setter schreiben oder ist es übertrieben?
unit-testing
Hector421
quelle
quelle
Antworten:
Ich würde nein sagen.
@ Will sagte, Sie sollten eine 100% ige Codeabdeckung anstreben, aber meiner Meinung nach ist das eine gefährliche Ablenkung. Sie können Unit-Tests schreiben, die zu 100% abgedeckt sind, und dennoch absolut nichts testen.
Unit-Tests dienen dazu, das Verhalten Ihres Codes auf ausdrucksstarke und aussagekräftige Weise zu testen, und Getter / Setter sind nur ein Mittel zum Zweck. Wenn Sie Tests mit den Gettern / Setzern durchführen, um das Ziel zu erreichen, die "echte" Funktionalität zu testen, ist das gut genug.
Wenn andererseits Ihre Getter und Setter mehr tun als nur zu bekommen und zu setzen (dh sie sind richtig komplexe Methoden), dann sollten sie getestet werden. Aber schreiben Sie keinen Unit-Testfall, nur um einen Getter oder Setter zu testen, das ist Zeitverschwendung.
quelle
Roy Osherove sagt in seinem berühmten Buch "The Art Of Unit Testing":
quelle
Ein klares JA mit TDD
Hinweis : Diese Antwort erhält immer wieder positive Stimmen, wenn auch möglicherweise ein schlechter Rat. Um zu verstehen warum, schauen Sie sich unten die kleine Schwester an .
Umstritten in Ordnung, aber ich würde argumentieren, dass jedem, der diese Frage mit „Nein“ beantwortet, ein grundlegendes Konzept von TDD fehlt.
Für mich ist die Antwort ein klares Ja, wenn Sie TDD folgen. Wenn Sie nicht sind, dann ist nein eine plausible Antwort.
Die DDD in TDD
TDD wird oft als Hauptvorteil bezeichnet.
Verantwortung von Umsetzung trennen
Als Programmierer ist es furchtbar verlockend, Attribute als etwas von Bedeutung und Getter und Setter als eine Art Overhead zu betrachten.
Attribute sind jedoch ein Implementierungsdetail, während Setter und Getter die vertragliche Schnittstelle sind, über die Programme tatsächlich funktionieren.
Es ist viel wichtiger zu buchstabieren, dass ein Objekt:
und
dann, wie dieser Zustand tatsächlich gespeichert wird (für die ein Attribut der häufigste, aber nicht der einzige Weg ist).
Ein Test wie
ist wichtig für den Dokumentationsteil von TDD.
Die Tatsache, dass die eventuelle Implementierung trivial ist (Attribut) und keinen Verteidigungsvorteil bietet , sollte Ihnen beim Schreiben des Tests unbekannt sein.
Der Mangel an Round-Trip-Technik ...
Eines der Hauptprobleme in der Welt der Systementwicklung ist das Fehlen von Round-Trip-Engineering 1 - Der Entwicklungsprozess eines Systems ist in unzusammenhängende Teilprozesse fragmentiert, deren Artefakte (Dokumentation, Code) häufig inkonsistent sind.
1 Brodie, Michael L. "John Mylopoulos: Samen der konzeptuellen Modellierung nähen." Konzeptionelle Modellierung: Grundlagen und Anwendungen. Springer Berlin Heidelberg, 2009. 1-9.
... und wie TDD es löst
Es ist der Dokumentationsteil von TDD, der sicherstellt, dass die Spezifikationen des Systems und seines Codes immer konsistent sind.
Zuerst entwerfen, später implementieren
Innerhalb von TDD schreiben wir zuerst einen fehlgeschlagenen Abnahmetest und erst dann den Code, der sie passieren lässt.
Innerhalb des übergeordneten BDD schreiben wir zuerst Szenarien und lassen sie dann bestehen.
Warum sollten Sie Setter und Getter ausschließen?
Theoretisch ist es innerhalb von TDD durchaus möglich, dass eine Person den Test schreibt und eine andere Person den Code implementiert, der ihn bestanden hat.
Fragen Sie sich also:
Da Getter und Setter eine öffentliche Schnittstelle zu einer Klasse sind, lautet die Antwort offensichtlich Ja , oder es gibt keine Möglichkeit, den Status eines Objekts festzulegen oder abzufragen. Der Weg, dies zu tun, besteht jedoch nicht unbedingt darin, jede Methode einzeln zu testen. Weitere Informationen finden Sie in meiner anderen Antwort .
Wenn Sie den Code zuerst schreiben, ist die Antwort möglicherweise nicht so eindeutig.
quelle
tl; dr: Ja, das solltest du , und mit OpenPojo ist es trivial.
Sie sollten einige Validierungen in Ihren Gettern und Setzern durchführen, damit Sie dies testen können. Zum Beispiel
setMom(Person p)
sollte es nicht erlaubt sein, jemanden, der jünger als sie selbst ist, als Mutter zu bestimmen.Selbst wenn Sie dies jetzt nicht tun, stehen die Chancen gut, dass Sie es in Zukunft tun werden, dann ist dies eine gute Möglichkeit für die Regressionsanalyse. Wenn Sie zulassen möchten, dass Mütter eingestellt werden,
null
sollten Sie einen Test dafür durchführen, falls jemand dies später ändert. Dies wird Ihre Annahmen verstärken.Ein häufiger Fehler ist,
void setFoo( Object foo ){ foo = foo; }
wo es sein solltevoid setFoo( Object foo ){ this.foo = foo; }
. (Im ersten Fallfoo
wird in den Parameter geschrieben, nicht in dasfoo
Feld des Objekts ).Wenn Sie ein Array oder eine Sammlung zurückgeben, sollten Sie testen, ob der Getter vor der Rückkehr defensive Kopien der an den Setter übergebenen Daten ausführen wird .
Wenn Sie die grundlegendsten Setter / Getter haben, werden durch Unit-Tests möglicherweise höchstens 10 Minuten pro Objekt hinzugefügt. Was ist also der Verlust? Wenn Sie Verhalten hinzufügen, haben Sie bereits einen Skeletttest und erhalten diesen Regressionstest kostenlos. Wenn Sie Java verwenden, haben Sie keine Entschuldigung, da es OpenPojo gibt . Es gibt eine Reihe von Regeln, die Sie aktivieren und dann Ihr gesamtes Projekt damit scannen können, um sicherzustellen, dass sie in Ihrem Code konsistent angewendet werden.
Aus ihren Beispielen :
quelle
Ja, aber nicht immer isoliert
Lassen Sie mich näher darauf eingehen:
Was ist ein Unit Test?
Von der effektiven Arbeit mit Legacy-Code 1 :
Beachten Sie, dass bei OOP, wo Sie Getter und Setter finden, die Einheit die Klasse ist , nicht unbedingt einzelne Methoden .
Was ist ein guter Test?
Alle Anforderungen und Tests folgen der Form der Hoare-Logik :
Wo:
{P}
ist die Voraussetzung ( gegeben )C
ist die Auslösebedingung ( wann ){Q}
ist die Nachbedingung ( dann )Dann kommt die Maxime:
Dies bedeutet, dass Sie nicht testen sollten, wie
C
die Nachbedingung erreicht wird. Sie sollten überprüfen, ob dies{Q}
das Ergebnis von istC
.Wenn es um OOP geht,
C
ist eine Klasse. Sie sollten also keine internen Effekte testen, sondern nur externe Effekte.Warum testen Sie Bean Getter und Setter nicht isoliert?
Getter und Setter können eine gewisse Logik beinhalten, aber solange diese Logik keine externe Wirkung hat, werden sie zu Bean-Accessoren. 2 ) Ein Test muss in das Objekt schauen und dadurch nicht nur die Kapselung verletzen, sondern auch die Implementierung testen.
Sie sollten Bean Getter und Setter also nicht isoliert testen. Das ist schlecht:
Obwohl , wenn
setVAT
eine Ausnahme auslösen würde, würde ein entsprechender Test , da nun angemessen sein ist ein externer Effekt.Wie sollten Sie Getter und Setter testen?
Es macht praktisch keinen Sinn, den internen Zustand eines Objekts zu ändern, wenn eine solche Änderung keine Auswirkung auf die Außenseite hat, selbst wenn eine solche Auswirkung später auftritt.
Ein Test für Setter und Getter sollte sich daher mit der externen Wirkung dieser Methoden befassen, nicht mit den internen.
Beispielsweise:
Sie können sich denken:
Bei einer
setVAT()
Fehlfunktion sollte dieser Test dennoch fehlschlagen.1 Feathers, M., 2004. Effektiv mit Legacy-Code arbeiten. Prentice Hall Professional.
2 Martin, RC, 2009. Sauberer Code: Ein Handbuch für agile Software-Handwerkskunst. Pearson Ausbildung.
quelle
Obwohl es berechtigte Gründe für Eigenschaften gibt, gibt es eine verbreitete Überzeugung, dass das Offenlegen des Mitgliedsstatus über Eigenschaften ein schlechtes Design ist. Robert Martins Artikel über das Open Closed-Prinzip erweitert dies, indem er besagt, dass Eigenschaften die Kopplung fördern und daher die Möglichkeit einschränken, eine Klasse vor Änderungen zu schließen. Wenn Sie die Eigenschaft ändern, müssen sich auch alle Verbraucher der Klasse ändern. Er qualifiziert, dass das Offenlegen von Mitgliedsvariablen nicht unbedingt ein schlechtes Design ist, sondern möglicherweise nur ein schlechter Stil. Wenn Eigenschaften jedoch schreibgeschützt sind, ist die Wahrscheinlichkeit von Missbrauch und Nebenwirkungen geringer.
Der beste Ansatz, den ich für Unit-Tests bereitstellen kann (und dies mag seltsam erscheinen), besteht darin, so viele Eigenschaften wie möglich geschützt oder intern zu machen. Dies verhindert das Koppeln und entmutigt das Schreiben alberner Tests für Getter und Setter.
Es gibt offensichtliche Gründe, warum Lese- / Schreibeigenschaften verwendet werden sollten, z. B. ViewModel-Eigenschaften, die an Eingabefelder gebunden sind usw.
In der Praxis sollten Unit-Tests die Funktionalität durch öffentliche Methoden steigern. Wenn der Code, den Sie testen, diese Eigenschaften verwendet, erhalten Sie eine kostenlose Codeabdeckung. Wenn sich herausstellt, dass diese Eigenschaften niemals durch Codeabdeckung hervorgehoben werden, besteht die sehr starke Möglichkeit, dass:
Wenn Sie Tests für Getter und Setter schreiben, erhalten Sie ein falsches Gefühl der Abdeckung und können nicht feststellen, ob die Eigenschaften tatsächlich vom Funktionsverhalten verwendet werden.
quelle
Wenn die zyklomatische Komplexität des Getters und / oder Setters 1 ist (was normalerweise der Fall ist), lautet die Antwort Nein, sollten Sie nicht.
Wenn Sie also keine SLA haben, die eine 100% ige Codeabdeckung erfordert, kümmern Sie sich nicht darum, den wichtigen Aspekt Ihrer Software zu testen.
PS Denken Sie daran, Getter und Setter zu unterscheiden, auch in Sprachen wie C #, in denen Eigenschaften möglicherweise gleich aussehen. Die Setter-Komplexität kann höher sein als die des Getters und somit einen Unit-Test validieren.
quelle
Eine humorvolle und doch weise Einstellung : Der Weg des Testivus
"Schreiben Sie den Test, den Sie heute können"
Das Testen von Gettern / Setzern kann übertrieben sein, wenn Sie ein erfahrener Tester sind und dies ein kleines Projekt ist. Wenn Sie jedoch gerade erst anfangen, das Testen von Einheiten zu lernen, oder diese Getter / Setter möglicherweise Logik enthalten (wie im
setMom()
Beispiel von @ ArtB ), ist es eine gute Idee, Tests zu schreiben.quelle
Dies war tatsächlich ein aktuelles Thema zwischen meinem Team und mir. Wir drehen für eine 80% ige Codeabdeckung. Mein Team argumentiert, dass Getter und Setter automatisch implementiert werden und der Compiler hinter den Kulissen einen grundlegenden Code generiert. In diesem Fall ist es angesichts des nicht aufdringlichen Codes nicht sinnvoll, den vom Compiler für Sie erstellten Code zu testen. Wir hatten auch diese Diskussion über asynchrone Methoden und in diesem Fall generiert der Compiler eine ganze Reihe von Code hinter den Kulissen. Dies ist ein anderer Fall und etwas, das wir testen. Lange Antwort kurz, sprechen Sie mit Ihrem Team darüber und entscheiden Sie selbst, ob es sich lohnt, es zu testen.
Wenn Sie den Code-Coverage-Bericht wie wir verwenden, können Sie auch das Attribut [ExcludeFromCodeCoverage] hinzufügen. Unsere Lösung bestand darin, dies für Modelle zu verwenden, die nur Eigenschaften haben, die Getter und Setter verwenden, oder für die Eigenschaft selbst. Auf diese Weise wird die gesamte Codeabdeckung in% nicht beeinflusst, wenn der Codeabdeckungsbericht ausgeführt wird, vorausgesetzt, Sie verwenden diese zur Berechnung Ihrer Codeabdeckungsprozentsätze. Viel Spaß beim Testen!
quelle
Meiner Meinung nach ist die Codeabdeckung ein guter Weg, um festzustellen, ob Sie Funktionen verpasst haben, die Sie abdecken sollten.
Wenn Sie die Abdeckung manuell anhand ihrer hübschen Farbe überprüfen, kann argumentiert werden, dass einfache Getter und Setter nicht getestet werden müssen (obwohl ich es immer tue).
Wenn Sie nur den Prozentsatz der Codeabdeckung in Ihrem Projekt überprüfen, ist ein Prozentsatz der Testabdeckung wie 80% bedeutungslos. Sie können alle nicht logischen Teile testen und einige wichtige Teile vergessen. In diesem Fall bedeutet nur 100%, dass Sie Ihren gesamten wichtigen Code (und auch den gesamten nicht logischen Code) getestet haben. Sobald es 99,9% sind, wissen Sie, dass Sie etwas vergessen haben.
Übrigens: Die Codeabdeckung ist die letzte Überprüfung, um festzustellen, ob Sie eine Klasse vollständig (als Einheit) getestet haben. Eine 100% ige Codeabdeckung bedeutet jedoch nicht unbedingt, dass Sie tatsächlich alle Funktionen der Klasse getestet haben. Unit-Tests sollten daher immer nach der Logik der Klasse durchgeführt werden. Am Ende führen Sie eine Berichterstattung durch, um festzustellen, ob Sie etwas vergessen haben. Wenn Sie es richtig gemacht haben, haben Sie beim ersten Mal 100% getroffen.
Noch etwas: Als ich kürzlich bei einer großen Bank in den Niederlanden arbeitete, bemerkte ich, dass Sonar eine 100% ige Codeabdeckung anzeigte. Ich wusste jedoch, dass etwas fehlte. Bei der Überprüfung der prozentualen Codeabdeckung pro Datei wurde eine Datei mit einem niedrigeren Prozentsatz angezeigt. Der gesamte Prozentsatz der Codebasis war so groß, dass für die eine Datei der Prozentsatz nicht als 99,9% angezeigt wurde. Vielleicht möchten Sie darauf achten ...
quelle
Ich habe eine kleine Analyse der Abdeckung durchgeführt, die im JUnit-Code selbst erzielt wurde .
Eine Kategorie von ungedecktem Code ist "zu einfach zu testen" . Dies schließt einfache Getter und Setter ein, die die Entwickler von JUnit nicht testen.
Auf der anderen Seite hat JUnit keine (nicht veraltete) Methode, die länger als 3 Zeilen ist und von keinem Test abgedeckt wird.
quelle
Ich würde sagen: JA Fehler in Getter / Setter-Methoden können sich lautlos einschleichen und einige hässliche Fehler verursachen.
Ich habe eine Bibliothek geschrieben, um diesen und einige andere Tests zu vereinfachen. Das einzige, was Sie in Ihren JUnit-Tests schreiben müssen, ist Folgendes:
-> https://github.com/Mixermachine/base-test
quelle
Ja, insbesondere wenn das abzurufende Element ein Objekt einer Klasse ist, die von einer abstrakten Klasse untergeordnet ist. Ihre IDE weist Sie möglicherweise darauf hin, dass eine bestimmte Eigenschaft nicht initialisiert wurde.
Und dann stürzt ein scheinbar nicht verwandter Test mit a ab,
NullPointerException
und es dauert eine Weile, bis Sie herausgefunden haben, dass eine gettable-Eigenschaft überhaupt nicht vorhanden ist.Das wäre aber immer noch nicht so schlimm wie das Problem in der Produktion zu entdecken.
Es könnte eine gute Idee sein, sicherzustellen, dass alle Ihre abstrakten Klassen Konstruktoren haben. Wenn nicht, macht Sie der Test eines Getters möglicherweise auf ein Problem aufmerksam.
Bei Gettern und Setzern von Grundelementen könnte die Frage lauten: Teste ich mein Programm oder teste ich die JVM oder CLR? Im Allgemeinen muss die JVM nicht getestet werden.
quelle