Code intern machen, aber für Unit-Tests aus anderen Projekten verfügbar machen

129

Wir setzen alle unsere Unit-Tests in ihre eigenen Projekte ein. Wir stellen fest, dass wir bestimmte Klassen nur für die Unit-Tests öffentlich anstatt intern veröffentlichen müssen. Gibt es sowieso zu vermeiden, dies tun zu müssen. Was bedeutet das Gedächtnis, wenn Klassen öffentlich statt versiegelt werden?

Leora
quelle
2
Mögliches Duplikat des C # "internen" Zugriffsmodifikators beim Testen von Einheiten
Dour High Arch

Antworten:

205

Wenn Sie .NET verwenden, können Sie mit dem Assembly-Attribut InternalsVisibleTo "Friend" -Baugruppen erstellen. Hierbei handelt es sich um bestimmte stark benannte Assemblys, die auf interne Klassen und Mitglieder der anderen Assembly zugreifen dürfen.

Beachten Sie, dass dies mit Diskretion verwendet werden sollte, da die beteiligten Baugruppen eng miteinander verbunden sind. InternalsVisibleTo wird häufig für Unit-Test-Projekte verwendet. Aus dem oben genannten Grund ist es wahrscheinlich keine gute Wahl für die Verwendung in Ihren tatsächlichen Anwendungsbaugruppen.

Beispiel:

[assembly: InternalsVisibleTo("NameAssemblyYouWantToPermitAccess")]
namespace NameOfYourNameSpace
{
Asche
quelle
23
Ich würde vorschlagen, ein #if DEBUG um das Attribut zu setzen und dann Unit-Tests beim Debuggen durchzuführen. Auf diese Weise stellen Sie sicher, dass das Attribut nicht im Release-Code festgelegt ist.
Steve Cook
Dies ist nur eine Idee, ich weiß nicht ... Wie wäre es mit: #if DEBUG öffentliche Klasse IniReader #else interne Klasse IniReader #endif Wahrscheinlich nicht empfohlen? Warum?
Jmelhus
4
Warum sollten Tests auf Debug-Builds beschränkt werden?
Marco Mp
2
Nur ein Trottel, es ist nicht erforderlich, dass die "Freund" -Baugruppen einen starken Namen haben (könnte eine gute Praxis sein - auch wenn mein persönlicher Geschmack etwas anderes sagt, aber nicht obligatorisch).
Marco Mp
9
Nicht zustimmen. Wenn ich eine komplexe Komponente mit einer sehr dünnen öffentlichen API erstelle, ist es unpraktisch und unrealistisch, nur über die öffentliche API zu testen. Sie werden mit einem nicht wartbaren Schlammball enden. Stattdessen würde ich die internen Einheiten sorgfältig definieren und separat testen. Wie Jeremy D. Miller ziemlich oft gesagt hat: "Teste klein, bevor du groß testest".
Dennis Doomen
6

Wenn es sich um eine interne Klasse handelt, darf sie nicht isoliert verwendet werden. Daher sollten Sie es nicht wirklich testen, außer eine andere Klasse zu testen, die dieses Objekt intern verwendet.

So wie Sie keine privaten Mitglieder einer Klasse testen sollten, sollten Sie auch keine internen Klassen einer DLL testen. Diese Klassen sind Implementierungsdetails einiger öffentlich zugänglicher Klassen und sollten daher durch andere Komponententests gut trainiert werden.

Die Idee ist, dass Sie nur das Verhalten einer Klasse testen möchten, denn wenn Sie interne Implementierungsdetails testen, sind Ihre Tests spröde. Sie sollten in der Lage sein, die Implementierungsdetails einer Klasse zu ändern, ohne alle Ihre Tests zu unterbrechen.

Wenn Sie feststellen, dass Sie diese Klasse wirklich testen müssen, sollten Sie erneut prüfen, warum diese Klasse überhaupt intern ist.

Josh
quelle
2
Implementierungsdetails sollten im Rahmen eines umfassenden Tests ausgeübt werden. Schauen Sie nicht auf private Variablen ... testen Sie das erwartete Verhalten. Wenn der Test richtig ist, sollten alle internen Leitungen und Verkabelungen als Teil davon getestet werden. Abgestimmt.
Gishu
69
Ich stimme dem nicht unbedingt zu, da diese Klassen für andere Klassen innerhalb der DLL "öffentlich" sind und die Funktionalität der Klasse unabhängig getestet werden sollte
Leora
27
Ich stimme auch nicht zu. Einheiten sind Einheiten und müssen isoliert getestet werden.
Sentinel
5
Tolle allgemeine Richtlinie, aber lasst uns nicht dogmatisch werden. Tests dienen zwei Hauptzwecken: 1) Regression - stellen Sie sicher, dass Sie nichts kaputt gemacht haben, 2) Erhöhen Sie die Entwicklungsgeschwindigkeit. Wenn ich jedes Mal, wenn ich eine Codezeile schreiben möchte, einen riesigen Service aufbauen muss, werde ich die Entwicklung behindern. Wenn ich ein komplexes internes Stück habe, möchte ich es isoliert entwickeln und testen können. Umgekehrt möchte ich nicht, dass alles isoliert getestet wird (wodurch der Wert des Regressionstests verringert oder der Testcode dupliziert wird), aber ein Teil des Codes rechtfertigt die Isolation. Vielleicht ist der Schlüssel, es zu einer separaten Baugruppe zu machen.
Lee Jensen
2
OP hier ist richtig. Sie koppeln Ihre Tests an die interne Implementierung. Daher ist Ihr Team für immer ein Sklave für die Reparatur der Tests und den schrecklichen Spottcode. Testen Sie nur öffentliche APIs, sei es die gepackte lib-API oder netzwerkfähige APIs. Der gesamte Code sollte über die Vordertür ausgeübt werden können. Das Testen der Implementierung ist der Grund, warum TDD weitgehend gestorben ist. Es ist nur eine riesige PITA, buchstäblich jede Klasse der gesamten App zu testen, anstatt sich darauf zu konzentrieren, das Verhalten des Systems sicherzustellen. Ich denke, fast jeder, einschließlich "maßgeblicher" Bücher, hat dies falsch gemacht oder nicht klargestellt.
Luke Puplett
4

zu Dokumentationszwecken

Alternativ können Sie die interne Klasse mithilfe der Type.GetTypeMethode instanziieren

Beispiel

//IServiceWrapper is public class which is 
//the same assembly with the internal class 
var asm = typeof(IServiceWrapper).Assembly;
//Namespace.ServiceWrapper is internal
var type = asm.GetType("Namespace.ServiceWrapper");
return (IServiceWrapper<T>)Activator
    .CreateInstance(type, new object[1] { /*constructor parameter*/ });

Für generische Typen gibt es verschiedene Verfahren wie unten:

var asm = typeof(IServiceWrapper).Assembly;
//note the name Namespace.ServiceWrapper`1
//this is for calling Namespace.ServiceWrapper<>
var type = asm.GetType("Namespace.ServiceWrapper`1");
var genType = type.MakeGenericType(new Type[1] { typeof(T) });
return (IServiceWrapper<T>)Activator
     .CreateInstance(genType, new object[1] { /*constructor parameter*/});
ktutnik
quelle
-5

Der Unterricht kann sowohl öffentlich als auch versiegelt sein.

Aber tu das nicht.

Sie können ein Tool zum Reflektieren interner Klassen erstellen und eine neue Klasse ausgeben, die über Reflektion auf alles zugreift. MSTest macht das.

Bearbeiten: Ich meine, wenn Sie kein Testmaterial in Ihre ursprüngliche Baugruppe aufnehmen möchten; Dies funktioniert auch, wenn die Mitglieder privat sind.

TraumaPony
quelle
1
Warte was? Du sagst, mach kein public sealed class? Was ist Ihre Begründung für dieses Juwel?
Crush
@ Crush Traumapony hat sich seit 2009 nicht mehr angemeldet.
Prof. Falken