Moderator Hinweis: Hier wurden bereits 39 Antworten veröffentlicht (einige wurden gelöscht). Überlegen Sie sich vor dem Veröffentlichen Ihrer Antwort, ob Sie der Diskussion etwas Sinnvolles hinzufügen können. Sie wiederholen höchstwahrscheinlich nur das, was bereits jemand anderes gesagt hat.
Gelegentlich muss ich eine private Methode in einer Klasse öffentlich machen, um einige Unit-Tests dafür zu schreiben.
Normalerweise liegt dies daran, dass die Methode Logik enthält, die von anderen Methoden in der Klasse gemeinsam genutzt wird, und dass es einfacher ist, die Logik selbst zu testen, oder dass ein anderer Grund möglich sein könnte, wenn ich die in synchronen Threads verwendete Logik testen möchte, ohne mir Gedanken über Threading-Probleme machen zu müssen .
Tun dies andere Leute, weil ich es nicht wirklich mag? Ich persönlich denke, dass die Boni die Probleme überwiegen, eine Methode öffentlich zu machen, die außerhalb der Klasse keinen wirklichen Service bietet ...
AKTUALISIEREN
Vielen Dank für die Antworten an alle, scheint das Interesse der Menschen geweckt zu haben. Ich denke, der allgemeine Konsens ist, dass das Testen über die öffentliche API erfolgen sollte, da dies die einzige Möglichkeit ist, eine Klasse jemals zu verwenden, und ich stimme dem zu. Die paar Fälle, die ich oben erwähnt habe, in denen ich dies oben tun würde, waren ungewöhnliche Fälle, und ich dachte, die Vorteile wären es wert.
Ich kann jedoch sehen, dass jeder darauf hinweist, dass es niemals wirklich passieren sollte. Und wenn ich ein bisschen mehr darüber nachdenke, denke ich, dass es eine schlechte Idee ist, Ihren Code zu ändern, um Tests zu ermöglichen - schließlich ist das Testen in gewisser Weise ein Support-Tool, und das Ändern eines Systems, um ein Support-Tool zu unterstützen, wenn Sie so wollen, ist offensichtlich schlechte Praxis.
quelle
@VisibileForTesting
Anmerkung, um solche Methoden zu erstellen. Ich empfehle Ihnen, dies zu tun, damit der Grund dafür, dass die Methode nichtprivate
ordnungsgemäß dokumentiert ist.Antworten:
Generell bin ich normalerweise alle dafür, "Produktions" -Code umzugestalten, um das Testen zu vereinfachen. Ich denke jedoch nicht, dass dies hier ein guter Anruf wäre. Ein guter Komponententest sollte sich (normalerweise) nicht um die Implementierungsdetails der Klasse kümmern, sondern nur um ihr sichtbares Verhalten. Anstatt die internen Stapel dem Test auszusetzen, können Sie testen, ob die Klasse die Seiten in der Reihenfolge zurückgibt, in der Sie sie nach dem Aufruf von
first()
oder erwartenlast()
.Betrachten Sie zum Beispiel diesen Pseudocode:
quelle
Persönlich würde ich lieber einen Unit-Test mit der öffentlichen API durchführen und die private Methode sicherlich nie öffentlich machen, nur um das Testen zu vereinfachen.
Wenn Sie die private Methode wirklich isoliert testen möchten, können Sie in Java Easymock / Powermock verwenden , um dies zu ermöglichen.
Sie müssen pragmatisch sein und sich auch der Gründe bewusst sein, warum es schwierig ist, Dinge zu testen.
" Hören Sie sich die Tests an " - wenn es schwierig zu testen ist, sagt Ihnen das etwas über Ihr Design aus? Könnten Sie umgestalten, wo ein Test für diese Methode trivial wäre und leicht durch Tests durch die öffentliche API abgedeckt werden könnte?
Hier ist, was Michael Feathers in " Effektiv mit Legacy-Code arbeiten " zu sagen hat.
quelle
Wie andere gesagt haben, ist es etwas verdächtig, überhaupt private Methoden zu testen; Unit-Test der öffentlichen Schnittstelle, nicht der privaten Implementierung Details.
Die Technik, die ich verwende, wenn ich in C # etwas Privates testen möchte, besteht darin, den Zugriffsschutz von privat auf intern herunterzustufen und dann die Unit-Test-Assembly mit InternalsVisibleTo als Friend-Assembly zu markieren . Die Einheitentestbaugruppe kann dann die Einbauten als öffentlich behandeln, Sie müssen sich jedoch keine Sorgen machen, dass Ihre öffentliche Oberfläche versehentlich vergrößert wird.
quelle
Viele Antworten schlagen vor, nur die öffentliche Schnittstelle zu testen, aber meiner Meinung nach ist dies unrealistisch. Wenn eine Methode 5 Schritte ausführt, sollten Sie diese fünf Schritte separat und nicht alle zusammen testen. Dies erfordert das Testen aller fünf Methoden, die (außer zum Testen) ansonsten möglich wären
private
.Die übliche Methode zum Testen von "privaten" Methoden besteht darin, jeder Klasse eine eigene Schnittstelle zuzuweisen und die "privaten" Methoden zu
public
erstellen, sie jedoch nicht in die Schnittstelle aufzunehmen. Auf diese Weise können sie zwar noch getestet werden, aber die Benutzeroberfläche wird nicht aufgebläht.Ja, dies führt zu einem Aufblähen von Dateien und Klassen.
Ja, dies macht die
public
undprivate
Bezeichner überflüssig.Ja, das ist ein Schmerz im Arsch.
Dies ist leider eines der vielen Opfer, die wir bringen, um Code testbar zu machen . Möglicherweise verfügt eine zukünftige Sprache (oder sogar eine zukünftige Version von C # / Java) über Funktionen, die das Testen von Klassen und Modulen komfortabler machen. aber in der Zwischenzeit müssen wir durch diese Reifen springen.
Es gibt einige, die argumentieren würden, dass jeder dieser Schritte eine eigene Klasse sein sollte , aber ich bin anderer Meinung - wenn sie alle den Status teilen, gibt es keinen Grund, fünf separate Klassen zu erstellen, in denen fünf Methoden funktionieren würden. Schlimmer noch, dies führt zu einem Aufblähen von Dateien und Klassen. Außerdem infiziert es die öffentliche API Ihres Moduls - all diese Klassen müssen es unbedingt sein,
public
wenn Sie sie von einem anderen Modul aus testen möchten (entweder das oder den Testcode in dasselbe Modul aufnehmen, was bedeutet, dass der Testcode mit Ihrem Produkt geliefert wird). .quelle
Ein Unit-Test sollte den öffentlichen Auftrag testen, die einzige Möglichkeit, wie eine Klasse in anderen Teilen des Codes verwendet werden kann. Eine private Methode besteht aus Implementierungsdetails. Sie sollten sie nicht testen. Soweit die öffentliche API ordnungsgemäß funktioniert, spielt die Implementierung keine Rolle und kann ohne Änderungen in Testfällen geändert werden.
quelle
IMO, Sie sollten Ihre Tests schreiben und keine tiefen Annahmen darüber treffen, wie Ihre Klasse im Inneren implementiert wurde. Sie möchten es wahrscheinlich später mit einem anderen internen Modell umgestalten, aber dennoch die gleichen Garantien geben, die die vorherige Implementierung bietet.
Vor diesem Hintergrund empfehle ich Ihnen, sich darauf zu konzentrieren, zu testen, ob Ihr Vertrag noch gültig ist, unabhängig davon, welche interne Implementierung Ihre Klasse derzeit hat. Eigenschaftsbasiertes Testen Ihrer öffentlichen APIs.
quelle
Wie wäre es mit einem privaten Paket? Dann kann Ihr Testcode es sehen (und andere Klassen in Ihrem Paket auch), aber es ist immer noch vor Ihren Benutzern verborgen.
Aber wirklich, Sie sollten keine privaten Methoden testen. Dies sind Implementierungsdetails und nicht Bestandteil des Vertrags. Alles, was sie tun, sollte durch Aufrufen der öffentlichen Methoden abgedeckt werden (wenn sie Code enthalten, der nicht von den öffentlichen Methoden ausgeübt wird, sollte dies geschehen). Wenn der private Code zu komplex ist, macht die Klasse wahrscheinlich zu viele Dinge und möchte nicht umgestaltet werden.
Eine Methode öffentlich zu machen ist eine große Verpflichtung. Sobald Sie dies tun, können die Benutzer es verwenden, und Sie können sie nicht mehr einfach ändern.
quelle
Aktualisieren : Ich habe diese Frage an zahlreichen anderen Stellen ausführlicher und vollständiger beantwortet. Dies ist auf meinem Blog zu finden .
Wenn ich jemals etwas öffentlich machen muss, um es zu testen, deutet dies normalerweise darauf hin, dass das zu testende System nicht dem folgt testende Prinzip Einzelverantwortung folgt . Daher fehlt eine Klasse, die eingeführt werden sollte. Nachdem Sie den Code in eine neue Klasse extrahiert haben, machen Sie ihn öffentlich. Jetzt können Sie problemlos testen und folgen SRP. Ihre andere Klasse muss diese neue Klasse einfach über Komposition aufrufen.
Das Veröffentlichen von Methoden / das Verwenden von Langauge-Tricks wie das Markieren von Code als sichtbar zum Testen von Baugruppen sollte immer das letzte Mittel sein.
Zum Beispiel:
Refaktorieren Sie dies, indem Sie ein Validatorobjekt einführen.
Jetzt müssen wir nur noch testen, ob der Validator korrekt aufgerufen wurde. Der tatsächliche Validierungsprozess (die zuvor private Logik) kann in reiner Isolation getestet werden. Es ist keine komplexe Testeinrichtung erforderlich, um sicherzustellen, dass diese Validierung erfolgreich ist.
quelle
Einige gute Antworten. Eine Sache, die ich nicht erwähnt habe, ist, dass bei der testgetriebenen Entwicklung (TDD) private Methoden während der Refactoring-Phase erstellt werden (siehe Extraktionsmethode für ein Beispiel für ein Refactoring-Muster) und daher bereits über die erforderliche Testabdeckung verfügen sollten . Wenn Sie es richtig machen (und natürlich erhalten Sie eine gemischte Menge an Meinungen, wenn es um Korrektheit geht), sollten Sie sich keine Sorgen machen müssen, dass Sie eine private Methode veröffentlichen müssen, nur damit Sie sie testen können.
quelle
Warum nicht den Stack-Management-Algorithmus in eine Utility-Klasse aufteilen? Die Utility-Klasse kann die Stapel verwalten und öffentliche Accessoren bereitstellen. Die Unit-Tests können sich auf Implementierungsdetails konzentrieren. Tiefe Tests für algorithmisch knifflige Klassen sind sehr hilfreich, um Randfälle zu falten und die Abdeckung sicherzustellen.
Dann kann Ihre aktuelle Klasse sauber an die Utility-Klasse delegieren, ohne Implementierungsdetails offenzulegen. Die Tests beziehen sich auf die Paginierungsanforderungen, die von anderen empfohlen wurden.
quelle
In Java gibt es auch die Möglichkeit, das Paket privat zu machen (dh den Sichtbarkeitsmodifikator wegzulassen). Wenn sich Ihre Komponententests im selben Paket wie die getestete Klasse befinden, sollte sie diese Methoden sehen können und ist etwas sicherer, als die Methode vollständig öffentlich zu machen.
quelle
Private Methoden werden normalerweise als "Hilfsmethoden" verwendet. Daher geben sie nur Grundwerte zurück und arbeiten niemals mit bestimmten Instanzen von Objekten.
Sie haben einige Optionen, wenn Sie sie testen möchten.
Alternativ können Sie eine neue Klasse mit der Hilfsmethode als öffentliche Methode erstellen, wenn sie für eine neue Klasse ausreichend ist.
Hierzu gibt es einen sehr guten Artikel .
quelle
Wenn Sie C # verwenden, können Sie die Methode intern machen. Auf diese Weise verschmutzen Sie die öffentliche API nicht.
Fügen Sie dann der DLL ein Attribut hinzu
[Assembly: InternalsVisibleTo ("MyTestAssembly")]
Jetzt sind alle Methoden in Ihrem MyTestAssembly-Projekt sichtbar. Vielleicht nicht perfekt, aber besser, als private Methoden öffentlich zu machen, nur um sie zu testen.
quelle
Verwenden Sie Reflection, um bei Bedarf auf die privaten Variablen zuzugreifen.
Aber wirklich, Sie interessieren sich nicht für den internen Status der Klasse, Sie möchten nur testen, ob die öffentlichen Methoden das zurückgeben, was Sie in den Situationen erwarten, die Sie erwarten können.
quelle
Ich würde sagen, es ist eine schlechte Idee, denn ich bin mir nicht sicher, ob Sie irgendwann Vorteile und potenzielle Probleme haben. Wenn Sie den Vertrag eines Aufrufs ändern, nur um eine private Methode zu testen, testen Sie die Klasse nicht in ihrer Verwendung, sondern erstellen ein künstliches Szenario, das Sie nie beabsichtigt hatten.
Wenn Sie die Methode als öffentlich deklarieren, können Sie in sechs Monaten (nachdem Sie vergessen haben, dass der einzige Grund für die Veröffentlichung einer Methode das Testen ist), dass Sie (oder wenn Sie das Projekt übergeben haben) eine völlig andere Person gewonnen haben Verwenden Sie es nicht, was zu möglicherweise unbeabsichtigten Folgen und / oder einem Wartungsalptraum führen kann.
quelle
In Bezug auf Unit-Tests sollten Sie definitiv keine weiteren Methoden hinzufügen. Ich glaube, Sie sollten besser einen Testfall über Ihre
first()
Methode erstellen, der vor jedem Test aufgerufen wird. - dann können Sie mehrere Male den Anrufnext()
,previous()
undlast()
zu sehen , ob die Ergebnisse Ihren Erwartungen entsprechen. Ich denke, wenn Sie Ihrer Klasse keine weiteren Methoden hinzufügen (nur zu Testzwecken), würden Sie sich an das "Black Box" -Prinzip des Testens halten.quelle
Überprüfen Sie zunächst, ob die Methode in eine andere Klasse extrahiert und veröffentlicht werden soll. Wenn dies nicht der Fall ist, machen Sie es paketgeschützt und kommentieren Sie es in Java mit @VisibleForTesting .
quelle
In Ihrem Update sagen Sie, dass es gut ist, nur mit der öffentlichen API zu testen. Hier gibt es eigentlich zwei Schulen.
Black-Box-Test
Die Black-Box-Schule sagt, dass die Klasse als Black-Box betrachtet werden sollte, die niemand in der Implementierung sehen kann. Der einzige Weg, dies zu testen, ist über die öffentliche API - genau wie die Benutzer der Klasse sie verwenden werden.
White-Box-Test.
Die White-Box-Schule hält es für selbstverständlich, das Wissen über die Implementierung der Klasse zu nutzen und die Klasse dann zu testen, um zu wissen, dass sie ordnungsgemäß funktioniert.
Ich kann mich wirklich nicht an der Diskussion beteiligen. Ich dachte nur, es wäre interessant zu wissen, dass es zwei verschiedene Möglichkeiten gibt, eine Klasse (oder eine Bibliothek oder was auch immer) zu testen.
quelle
Es gibt tatsächlich Situationen, in denen Sie dies tun sollten (z. B. wenn Sie einige komplexe Algorithmen implementieren). Tun Sie es einfach paketprivat und das wird ausreichen. Aber in den meisten Fällen haben Sie wahrscheinlich zu komplexe Klassen, was das Ausklammern der Logik in andere Klassen erfordert.
quelle
Private Methoden, die Sie isoliert testen möchten, sind ein Hinweis darauf, dass in Ihrer Klasse ein anderes "Konzept" vergraben ist. Extrahieren Sie dieses "Konzept" in eine eigene Klasse und testen Sie es als separate "Einheit".
Schauen Sie sich dieses Video an, um eine wirklich interessante Sicht auf das Thema zu erhalten.
quelle
Sie sollten niemals zulassen, dass Ihre Tests Ihren Code diktieren. Ich spreche nicht über TDD oder andere DDs, genau das, was Sie fragen. Benötigt Ihre App diese Methoden, um öffentlich zu sein? Wenn ja, testen Sie sie. Wenn dies nicht der Fall ist, machen Sie sie nicht nur zum Testen öffentlich. Gleiches gilt für Variablen und andere. Lassen Sie die Anforderungen Ihrer Anwendung den Code bestimmen und Ihre Tests testen, ob die Anforderungen erfüllt sind. (Wiederum meine ich nicht, zuerst zu testen oder nicht, ich meine, eine Klassenstruktur zu ändern, um ein Testziel zu erreichen).
Stattdessen sollten Sie "höher testen". Testen Sie die Methode, die die private Methode aufruft. Ihre Tests sollten jedoch Ihre Anwendungsanforderungen testen und nicht Ihre "Implementierungsentscheidungen".
Zum Beispiel (Bod Pseudo Code hier);
Es gibt keinen Grund, "Hinzufügen" zu testen. Sie können stattdessen "Bücher" testen.
Lassen Sie Ihre Tests niemals Entscheidungen zum Code-Design für Sie treffen. Testen Sie, ob Sie das erwartete Ergebnis erhalten, nicht wie Sie dieses Ergebnis erhalten.
quelle
Ich stimme eher zu, dass die Boni für das Testen der Einheit die Probleme überwiegen, die Sichtbarkeit einiger Mitglieder zu erhöhen. Eine leichte Verbesserung besteht darin, es geschützt und virtuell zu machen und es dann in einer Testklasse zu überschreiben, um es verfügbar zu machen.
Wenn es sich um eine Funktionalität handelt, die Sie separat testen möchten, schlägt dies nicht ein fehlendes Objekt in Ihrem Design vor? Vielleicht könnten Sie es in eine separate testbare Klasse einordnen ... dann delegiert Ihre vorhandene Klasse nur an eine Instanz dieser neuen Klasse.
quelle
Ich behalte die Testklassen im Allgemeinen im selben Projekt / in derselben Assembly wie die zu testenden Klassen.
Auf diese Weise brauche ich nur
internal
Sichtbarkeit, um Funktionen / Klassen testbar zu machen.Dies verkompliziert Ihren Bauprozess etwas, der die Testklassen herausfiltern muss. Dies erreiche ich, indem ich alle meine Testklassen benenne
TestedClassTest
Testklassen benenne und diese Klassen mit einem regulären Ausdruck filtere.Dies gilt natürlich nur für den C # / .NET-Teil Ihrer Frage
quelle
Ich werde oft eine Methode namens etwas wie hinzufügen
validate
,verify
,check
, usw., zu einer Klasse , so dass er den internen Zustand eines Objekts zu testen aufgerufen werden kann.Manchmal wird diese Methode in einen ifdef-Block eingeschlossen (ich schreibe hauptsächlich in C ++), damit sie nicht für die Veröffentlichung kompiliert wird. In Releases ist es jedoch häufig hilfreich, Validierungsmethoden bereitzustellen, mit denen die Objektbäume des Programms überprüft werden.
quelle
Guava verfügt über eine Annotation @VisibleForTesting zum Markieren von Methoden, deren Umfang (Paket oder öffentlich) erweitert wurde, als dies sonst der Fall wäre. Ich verwende eine @ Private-Annotation für dieselbe Sache.
Während die öffentliche API getestet werden muss, ist es manchmal bequem und sinnvoll, an Dinge heranzukommen, die normalerweise nicht öffentlich sind.
Wann:
Es scheint, als ob Religion die Technik übertrumpft.
quelle
Normalerweise lasse ich diese Methoden als
protected
und platziere den Komponententest im selben Paket (aber in einem anderen Projekt oder Quellordner), wo sie auf alle geschützten Methoden zugreifen können, da der Klassenlader sie im selben Namespace platziert.quelle
Nein, denn es gibt bessere Möglichkeiten, diese Katze zu häuten.
Einige Unit-Test-Kabelbäume basieren auf Makros in der Klassendefinition, die automatisch erweitert werden, um im Testmodus Hooks zu erstellen. Sehr C-Stil, aber es funktioniert.
Eine einfachere OO-Redewendung besteht darin, alles, was Sie testen möchten, "geschützt" anstatt "privat" zu machen. Das Testkabel erbt von der zu testenden Klasse und kann dann auf alle geschützten Mitglieder zugreifen.
Oder Sie wählen die Option "Freund". Persönlich ist dies die Funktion von C ++, die ich am wenigsten mag, weil sie gegen die Kapselungsregeln verstößt, aber es ist zufällig notwendig, wie C ++ einige Funktionen implementiert, also hey ho.
Wenn Sie Unit-Tests durchführen, müssen Sie diesen Mitgliedern wahrscheinlich auch Werte hinzufügen. White-Box-SMS ist vollkommen gültig. Und das würde Ihre Kapselung wirklich zerstören.
quelle
In .Net gibt es eine spezielle Klasse namens
PrivateObject
deigned, mit der Sie auf private Methoden einer Klasse zugreifen können.Weitere Informationen finden Sie im MSDN oder hier im Stapelüberlauf
(Ich frage mich, dass es bisher niemand erwähnt hat.)
Es gibt Situationen, in denen dies nicht ausreicht. In diesem Fall müssen Sie Reflexion verwenden.
Trotzdem würde ich mich an die allgemeine Empfehlung halten, keine privaten Methoden zu testen, aber wie immer gibt es immer Ausnahmen.
quelle
Wie in den Kommentaren anderer ausführlich erwähnt, sollten sich Unit-Tests auf die öffentliche API konzentrieren. Abgesehen von Vor- und Nachteilen und Begründungen können Sie private Methoden in einem Komponententest mithilfe von Reflexion aufrufen. Sie müssten natürlich sicherstellen, dass Ihre JRE-Sicherheit dies zulässt. Das Aufrufen privater Methoden wird vom Spring Framework mit seinen ReflectionUtils verwendet (siehe die
makeAccessible(Method)
Methode).Hier ist eine kleine Beispielklasse mit einer privaten Instanzmethode.
Und eine Beispielklasse, die die Methode der privaten Instanz ausführt.
Doing something private.
Wenn Sie B ausführen, wird gedruckt. Wenn Sie es wirklich benötigen, kann Reflection in Komponententests verwendet werden, um auf Methoden für private Instanzen zuzugreifen.quelle
Persönlich habe ich die gleichen Probleme beim Testen privater Methoden, und dies liegt daran, dass einige der Testtools begrenzt sind. Es ist nicht gut, wenn Ihr Design von begrenzten Werkzeugen angetrieben wird, wenn diese nicht auf Ihre Bedürfnisse reagieren. Ändern Sie das Werkzeug und nicht das Design. Da Sie nach C # fragen, kann ich keine guten Testtools vorschlagen, aber für Java gibt es zwei leistungsstarke Tools: TestNG und PowerMock, und Sie können entsprechende Testtools für die .NET-Plattform finden
quelle