Ich baue eine Klassenbibliothek auf, die einige öffentliche und private Methoden enthält. Ich möchte in der Lage sein, die privaten Methoden zu testen (hauptsächlich während der Entwicklung, aber auch für zukünftige Umgestaltungen).
Was ist der richtige Weg, um dies zu tun?
.net
unit-testing
tdd
private
Eric Labashosky
quelle
quelle
pre-historic
in Bezug auf Internetjahre ... ist , aber das Testen von privaten Methoden in Einheiten ist jetzt sowohl einfach als auch unkompliziert, da Visual Studio bei Bedarf und bei Bedarf die erforderlichen Accessor-Klassen erstellt Vorfüllen der Testlogik mit Ausschnitten, die fast dem entsprechen, was man sich für einfache Funktionstests wünscht. Siehe z. msdn.microsoft.com/en-us/library/ms184807%28VS.90%29.aspxAntworten:
Wenn Sie .net verwenden, sollten Sie das InternalsVisibleToAttribute verwenden .
quelle
#if DEBUG
dasInternalsVisibleTo
Attribut nicht verwenden , um es nicht auf den Release-Code anzuwenden?#if RELEASE_TEST
umInternalsVisibleTo
wie Mike vermuten läßt, und eine Kopie Ihrer Release - Build - Konfiguration vornehmen , dass definiertRELEASE_TEST
. Sie können Ihren Release-Code mit Optimierungen testen, aber wenn Sie tatsächlich für das Release erstellen, werden Ihre Tests weggelassen.Wenn Sie eine private Methode testen möchten, stimmt möglicherweise etwas nicht. Unit-Tests sollen (im Allgemeinen) die Schnittstelle einer Klasse testen, dh ihre öffentlichen (und geschützten) Methoden. Sie können natürlich eine Lösung dafür "hacken" (auch wenn Sie nur die Methoden öffentlich machen), aber Sie möchten möglicherweise auch Folgendes berücksichtigen:
quelle
Es ist möglicherweise nicht sinnvoll, private Methoden zu testen. Manchmal rufe ich aber auch gerne private Methoden aus Testmethoden auf. Meistens, um eine Codeduplizierung für die Testdatengenerierung zu verhindern ...
Microsoft bietet hierfür zwei Mechanismen an:
Accessoren
Der Mechanismus ist jedoch manchmal etwas unlösbar, wenn es um Änderungen der Schnittstelle der ursprünglichen Klasse geht. Daher vermeide ich es meistens, dies zu verwenden.
PrivateObject-Klasse Die andere Möglichkeit besteht darin, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject zu verwenden
quelle
Ich stimme der Philosophie "Sie sollten nur daran interessiert sein, die externe Schnittstelle zu testen" nicht zu. Es ist ein bisschen so, als würde man sagen, dass eine Autowerkstatt nur Tests durchführen sollte, um festzustellen, ob sich die Räder drehen. Ja, letztendlich interessiert mich das externe Verhalten, aber ich mag es, wenn meine eigenen, privaten, internen Tests etwas spezifischer und auf den Punkt gebracht werden. Ja, wenn ich refaktoriere, muss ich möglicherweise einige der Tests ändern, aber wenn es sich nicht um einen massiven Refaktor handelt, muss ich nur einige ändern, und die Tatsache, dass die anderen (unveränderten) internen Tests noch funktionieren, ist ein guter Indikator dafür Das Refactoring war erfolgreich.
Sie können versuchen, alle internen Fälle nur über die öffentliche Schnittstelle abzudecken. Theoretisch ist es möglich, jede interne Methode (oder zumindest jede wichtige) vollständig über die öffentliche Schnittstelle zu testen. Möglicherweise müssen Sie jedoch auf dem Kopf stehen, um dies zu erreichen Dies und die Verbindung zwischen den Testfällen, die über die öffentliche Schnittstelle ausgeführt werden, und dem internen Teil der Lösung, die getestet werden soll, ist möglicherweise schwer oder gar nicht zu erkennen. Einzelne Tests, die sicherstellen, dass die internen Maschinen ordnungsgemäß funktionieren, sind die geringfügigen Teständerungen wert, die beim Refactoring auftreten - zumindest war dies meine Erfahrung. Wenn Sie bei jedem Refactoring große Änderungen an Ihren Tests vornehmen müssen, ist dies möglicherweise nicht sinnvoll, aber in diesem Fall sollten Sie Ihr Design möglicherweise komplett überdenken.
quelle
FooService
, die zu tun hatX
, sollten Sie sich nur darum kümmern, dass dies tatsächlich der Fall ist,X
wenn Sie dazu aufgefordert werden. Wie es geht, sollte keine Rolle spielen. Wenn es Probleme in der Klasse gibt, die über die Schnittstelle nicht erkennbar sind (unwahrscheinlich), ist sie immer noch gültigFooService
. Wenn es sich um ein Problem handelt, das über die Benutzeroberfläche sichtbar ist , sollte es bei einem Test an öffentlichen Mitgliedern erkannt werden. Der springende Punkt sollte sein, dass das Rad, solange es sich richtig dreht, als Rad verwendet werden kann.PrivMethod
, ein Test, beiPubMethod
dem AnrufePrivMethod
es aufdecken sollten? Was passiert, wenn Sie IhreSimpleSmtpService
zu a ändernGmailService
? Plötzlich zeigen Ihre privaten Tests auf Code, der nicht mehr existiert oder möglicherweise anders funktioniert und fehlschlagen würde, obwohl die Anwendung möglicherweise perfekt wie geplant funktioniert. Wenn es eine komplexe Verarbeitung gibt, die für beide E-Mail-Absender gelten würde, sollte sie sich möglicherweise in einerEmailProcessor
befinden, die von beiden verwendet und separat getestet werden kann.In den seltenen Fällen, in denen ich private Funktionen testen wollte, habe ich sie normalerweise so geändert, dass sie stattdessen geschützt sind, und ich habe eine Unterklasse mit einer öffentlichen Wrapper-Funktion geschrieben.
Die Klasse:
Unterklasse zum Testen:
quelle
Ich denke, eine grundlegendere Frage sollte gestellt werden: Warum versuchen Sie überhaupt, die private Methode zu testen? Dies ist ein Codegeruch, bei dem Sie versuchen, die private Methode über die öffentliche Schnittstelle dieser Klasse zu testen, während diese Methode aus einem bestimmten Grund privat ist, da es sich um ein Implementierungsdetail handelt. Man sollte sich nur mit dem Verhalten der öffentlichen Schnittstelle befassen, nicht mit der Art und Weise, wie sie unter dem Deckmantel implementiert wird.
Wenn ich das Verhalten der privaten Methode mithilfe allgemeiner Refactorings testen möchte, kann ich ihren Code in eine andere Klasse extrahieren (möglicherweise mit Sichtbarkeit auf Paketebene, um sicherzustellen, dass er nicht Teil einer öffentlichen API ist). Ich kann dann sein Verhalten isoliert testen.
Das Produkt des Refactorings bedeutet, dass die private Methode jetzt eine separate Klasse ist, die zu einem Kollaborateur der ursprünglichen Klasse geworden ist. Sein Verhalten wird durch eigene Unit-Tests gut verstanden worden sein.
Ich kann mich dann über sein Verhalten lustig machen, wenn ich versuche, die ursprüngliche Klasse zu testen, damit ich mich darauf konzentrieren kann, das Verhalten der öffentlichen Schnittstelle dieser Klasse zu testen, anstatt eine kombinatorische Explosion der öffentlichen Schnittstelle und das Verhalten aller ihrer privaten Methoden testen zu müssen .
Ich sehe das analog zum Autofahren. Wenn ich ein Auto fahre, fahre ich nicht mit geöffneter Motorhaube, damit ich sehen kann, dass der Motor funktioniert. Ich verlasse mich auf die Schnittstelle, die das Auto bietet, nämlich den Drehzahlmesser und den Tacho, um zu wissen, dass der Motor funktioniert. Ich verlasse mich darauf, dass sich das Auto tatsächlich bewegt, wenn ich das Gaspedal drücke. Wenn ich den Motor testen möchte, kann ich das isoliert überprüfen. : D.
Natürlich kann das direkte Testen privater Methoden ein letzter Ausweg sein, wenn Sie eine Legacy-Anwendung haben, aber ich würde es vorziehen, wenn Legacy-Code überarbeitet wird, um bessere Tests zu ermöglichen. Michael Feathers hat zu diesem Thema ein großartiges Buch geschrieben. http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052
quelle
Private Typen, Interna und private Mitglieder sind aus irgendeinem Grund so, und oft möchten Sie sich nicht direkt mit ihnen anlegen. Und wenn Sie dies tun, besteht die Möglichkeit, dass Sie später brechen, da es keine Garantie gibt, dass die Leute, die diese Assemblys erstellt haben, die privaten / internen Implementierungen als solche behalten.
Aber manchmal, wenn ich einige Hacks / Erkundungen von kompilierten Assemblys oder Assemblys von Drittanbietern durchführe, wollte ich selbst eine private Klasse oder eine Klasse mit einem privaten oder internen Konstruktor initialisieren. Oder manchmal, wenn ich mich mit vorkompilierten Legacy-Bibliotheken befasse, die ich nicht ändern kann, schreibe ich am Ende einige Tests gegen eine private Methode.
So entstand der AccessPrivateWrapper - http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html - eine schnelle Wrapper-Klasse, die die Arbeit mit dynamischen C # 4.0-Funktionen und vereinfacht.
Sie können interne / private Typen wie erstellen
quelle
Nun, Sie können die private Methode auf zwei Arten testen
Sie können eine
PrivateObject
Klasseninstanz erstellen. Die Syntax lautet wie folgtSie können Reflexion verwenden.
quelle
PrivateClass
und diese verwenden. stackoverflow.com/questions/9122708/…Ich habe auch die InternalsVisibleToAttribute-Methode verwendet. Erwähnenswert ist auch, dass wenn Sie sich unwohl fühlen, Ihre zuvor privaten Methoden intern zu machen, um dies zu erreichen, diese möglicherweise ohnehin nicht Gegenstand direkter Komponententests sein sollten.
Schließlich testen Sie das Verhalten Ihrer Klasse und nicht die spezifische Implementierung. Sie können die letztere ändern, ohne die erstere zu ändern, und Ihre Tests sollten weiterhin bestehen.
quelle
Es gibt zwei Arten von privaten Methoden. Statische private Methoden und nicht statische private Methoden (Instanzmethoden). In den folgenden 2 Artikeln wird anhand von Beispielen erläutert, wie private Methoden einem Unit-Test unterzogen werden.
quelle
In MS Test ist eine nette Funktion integriert, die private Mitglieder und Methoden im Projekt verfügbar macht, indem eine Datei namens VSCodeGenAccessors erstellt wird
Mit Klassen, die von BaseAccessor abgeleitet sind
sowie
quelle
In CodeProject gibt es einen Artikel, in dem kurz die Vor- und Nachteile des Testens privater Methoden erläutert werden. Anschließend wird ein Reflektionscode für den Zugriff auf private Methoden bereitgestellt (ähnlich dem oben von Marcus bereitgestellten Code). Das einzige Problem, das ich im Beispiel festgestellt habe, ist, dass der Code überladene Methoden nicht berücksichtigt.
Den Artikel finden Sie hier:
http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx
quelle
Deklarieren Sie sie
internal
und verwenden Sie dann die Option ,InternalsVisibleToAttribute
damit Ihre Unit-Test-Baugruppe sie sehen kann.quelle
Ich neige dazu, keine Compiler-Direktiven zu verwenden, weil sie die Dinge schnell durcheinander bringen. Eine Möglichkeit, dies zu verringern, wenn Sie sie wirklich benötigen, besteht darin, sie in eine Teilklasse einzuteilen und Ihren Build diese CS-Datei bei der Erstellung der Produktionsversion ignorieren zu lassen.
quelle
Sie sollten die privaten Methoden Ihres Codes überhaupt nicht testen. Sie sollten die 'öffentliche Schnittstelle' oder API testen, die öffentlichen Dinge Ihrer Klassen. Die API sind alle öffentlichen Methoden, die Sie externen Anrufern zur Verfügung stellen.
Der Grund dafür ist, dass Sie, sobald Sie mit dem Testen der privaten Methoden und Interna Ihrer Klasse beginnen, die Implementierung Ihrer Klasse (die privaten Dinge) mit Ihren Tests koppeln. Dies bedeutet, dass Sie bei der Änderung Ihrer Implementierungsdetails auch Ihre Tests ändern müssen.
Aus diesem Grund sollten Sie die Verwendung von InternalsVisibleToAtrribute vermeiden.
Hier ist ein großartiger Vortrag von Ian Cooper, der dieses Thema behandelt: Ian Cooper: TDD, wo ist alles schief gelaufen?
quelle
Manchmal kann es sinnvoll sein, private Erklärungen zu testen. Grundsätzlich verfügt ein Compiler nur über eine öffentliche Methode: Compile (Zeichenfolge outputFileName, params string [] sourceSFileNames). Ich bin sicher, Sie verstehen, dass es schwierig wäre, eine solche Methode zu testen, ohne jede "versteckte" Deklaration zu testen!
Aus diesem Grund haben wir Visual T #: erstellt, um Tests zu vereinfachen. Es ist eine kostenlose .NET-Programmiersprache (C # v2.0-kompatibel).
Wir haben den Operator '.-' hinzugefügt. Es verhält sich einfach wie '.' Operator, außer Sie können auch auf versteckte Deklarationen aus Ihren Tests zugreifen, ohne etwas in Ihrem getesteten Projekt zu ändern.
Schauen Sie sich unsere Website an: Laden Sie sie kostenlos herunter .
quelle
Ich bin überrascht, dass dies noch niemand gesagt hat, aber eine Lösung, die ich angewendet habe, besteht darin, innerhalb der Klasse eine statische Methode zu erstellen, um sich selbst zu testen. Auf diese Weise haben Sie Zugriff auf alles Öffentliche und Private zum Testen.
Darüber hinaus können Sie in einer Skriptsprache (mit OO-Fähigkeiten wie Python, Ruby und PHP) den Dateitest selbst ausführen, wenn er ausgeführt wird. Eine gute schnelle Möglichkeit, um sicherzustellen, dass Ihre Änderungen nichts kaputt machen. Dies ist offensichtlich eine skalierbare Lösung zum Testen aller Klassen: Führen Sie sie einfach alle aus. (Sie können dies auch in anderen Sprachen mit einer ungültigen Hauptleitung tun, die immer auch ihre Tests ausführt).
quelle
Ich möchte hier ein klares Codebeispiel erstellen, das Sie für jede Klasse verwenden können, in der Sie die private Methode testen möchten.
Nehmen Sie diese Methoden in Ihre Testfallklasse auf und wenden Sie sie dann wie angegeben an.
$ this -> _ callMethod ('_ someFunctionName', Array (param1, param2, param3));
Geben Sie die Parameter einfach in der Reihenfolge aus, in der sie in der ursprünglichen privaten Funktion angezeigt werden
quelle
Für alle, die private Methoden ohne viel Aufwand ausführen möchten. Dies funktioniert mit jedem Unit-Test-Framework, das nur gute alte Reflection verwendet.
Dann können Sie in Ihren eigentlichen Tests Folgendes tun:
quelle
MbUnit hat dafür einen schönen Wrapper namens Reflector bekommen.
Sie können auch Werte aus Eigenschaften festlegen und abrufen
In Bezug auf den "Test privat" stimme ich zu, dass .. in der perfekten Welt. Es macht keinen Sinn, private Unit-Tests durchzuführen. In der realen Welt möchten Sie möglicherweise private Tests schreiben, anstatt Code umzugestalten.
quelle
Reflector
wurde durch die leistungsstärkereMirror
in Gallio / MbUnit v3.2 ersetzt. ( gallio.org/wiki/doku.php?id=mbunit:mirror )Hier ist ein guter Artikel über Unit-Tests von privaten Methoden. Ich bin mir jedoch nicht sicher, was besser ist, um Ihre Anwendung speziell für Tests zu entwickeln (es ist wie das Erstellen von Tests nur zum Testen) oder Reflexion zum Testen zu verwenden. Ich bin mir ziemlich sicher, dass die meisten von uns den zweiten Weg wählen werden.
quelle
Meiner Meinung nach sollten Sie nur die öffentliche API Ihrer Klasse testen.
Wenn Sie eine Methode veröffentlichen, um sie einem Unit-Test zu unterziehen, wird die Kapselung unterbrochen, um Implementierungsdetails freizulegen.
Eine gute öffentliche API löst ein unmittelbares Ziel des Client-Codes und löst dieses Ziel vollständig.
quelle
Ich benutze die PrivateObject- Klasse. Aber wie bereits erwähnt besser, um das Testen privater Methoden zu vermeiden.
quelle
"CC" ist der Befehlszeilen-Compiler auf dem von mir verwendeten System.
-Dfoo=bar
macht das Äquivalent von#define foo bar
. Diese Kompilierungsoption ändert also effektiv alle privaten Inhalte in öffentliche.quelle
Hier ist ein Beispiel, zuerst die Methodensignatur:
Hier ist der Test:
quelle
Eine Möglichkeit, dies zu tun, besteht darin, Ihre Methode zu haben
protected
und ein Testgerät zu schreiben, das Ihre zu testende Klasse erbt. Auf diese Weise drehen Sie Ihre Methode nichtpublic
, aber Sie aktivieren das Testen.quelle
1) Wenn Sie einen Legacy-Code haben, können Sie private Methoden nur durch Reflektion testen.
2) Wenn es sich um neuen Code handelt, haben Sie folgende Möglichkeiten:
Ich bevorzuge die einfachste und am wenigsten komplizierte Anmerkungsmethode. Das einzige Problem ist, dass wir die Sichtbarkeit erhöht haben, was meiner Meinung nach kein großes Problem darstellt. Wir sollten immer für die Schnittstelle codieren. Wenn wir also eine Schnittstelle MyService und eine Implementierung MyServiceImpl haben, können wir die entsprechenden Testklassen MyServiceTest (Testschnittstellenmethoden) und MyServiceImplTest (private Testmethoden) haben. Alle Clients sollten sowieso die Schnittstelle verwenden, so dass es eigentlich keine Rolle spielen sollte, obwohl die Sichtbarkeit der privaten Methode erhöht wurde.
quelle
Sie können es auch als öffentlich oder intern deklarieren (mit InternalsVisibleToAttribute), während Sie im Debug-Modus erstellen:
Es bläht den Code auf, wird aber
private
in einem Release-Build sein.quelle
Sie können die Testmethode für die private Methode in Visual Studio 2008 generieren. Wenn Sie einen Komponententest für eine private Methode erstellen, wird Ihrem Testprojekt ein Ordner "Test References" und diesem Ordner ein Accessor hinzugefügt. Auf den Accessor wird auch in der Logik der Einheitentestmethode Bezug genommen. Mit diesem Accessor kann Ihr Komponententest private Methoden in dem Code aufrufen, den Sie testen. Für Details schauen Sie sich an
http://msdn.microsoft.com/en-us/library/bb385974.aspx
quelle
Beachten Sie auch, dass für InternalsVisibleToAtrribute eine starke Benennung Ihrer Assembly erforderlich ist. Dies führt zu eigenen Problemen, wenn Sie an einer Lösung arbeiten, für die diese Anforderung zuvor noch nicht bestand. Ich benutze den Accessor, um private Methoden zu testen. In dieser Frage finden Sie ein Beispiel dafür.
quelle
InternalsVisibleToAttribute
erfordert nicht , dass Ihre Assemblys einen starken Namen haben. Ich verwende es derzeit für ein Projekt, bei dem dies nicht der Fall ist.