it-swarm-eu.dev

Ist statisch allgemein "böse" für Unit-Tests und wenn ja, warum empfiehlt Resharper es?

Ich habe festgestellt, dass es nur drei Möglichkeiten gibt, Abhängigkeiten zu testen (Mock/Stub), die in C # .NET statisch sind:

Angesichts der Tatsache, dass zwei davon nicht kostenlos sind und einer Release 1.0 noch nicht erreicht hat, ist es nicht einfach, sich über statische Dinge lustig zu machen.

Macht das statische Methoden und so "böse" (im Sinne von Unit-Tests)? Und wenn ja, warum soll Resharper, dass ich etwas mache, das statisch oder statisch sein kann? (Angenommen, Resharper ist nicht auch "böse".)

Erläuterung: Ich spreche über das Szenario, in dem Sie eine Methode einem Unit-Test unterziehen möchten und diese Methode eine statische Methode in a aufruft. unterschiedliche Einheit/Klasse. Wenn Sie nach den meisten Definitionen des Komponententests die zu testende Methode nur die statische Methode in der anderen Einheit/Klasse aufrufen lassen, sind Sie kein Komponententest. Sie testen die Integration . (Nützlich, aber kein Komponententest.)

87
Vaccano

Wenn ich mir die anderen Antworten hier anschaue, denke ich, dass es eine gewisse Verwechslung zwischen statischen Methoden geben kann, die einen statischen Zustand haben oder Nebenwirkungen verursachen (was für mich eine wirklich schlechte Idee ist), und statischen Methoden, die lediglich einen Wert zurückgeben.

Statische Methoden, die keinen Zustand haben und keine Nebenwirkungen verursachen, sollten leicht einheitlich testbar sein. Tatsächlich betrachte ich solche Methoden als eine "arme" Form der funktionalen Programmierung; Sie übergeben der Methode ein Objekt oder einen Wert und sie gibt ein Objekt oder einen Wert zurück. Nichts mehr. Ich sehe nicht ein, wie sich solche Methoden überhaupt negativ auf Unit-Tests auswirken würden.

108
Robert Harvey

Sie scheinen statisch Daten und statisch Methoden zu verwechseln. Wenn ich mich richtig erinnere, empfiehlt Resharper, private Methoden innerhalb einer Klasse statisch zu machen, wenn dies möglich ist. Ich glaube, dies bringt einen kleinen Leistungsvorteil. Es nicht empfehlen, "alles, was sein kann" statisch zu machen!

An statischen Methoden ist nichts auszusetzen und sie sind einfach zu testen (solange sie keine statischen Daten ändern). Stellen Sie sich zum Beispiel eine Mathematikbibliothek vor, die sich gut für eine statische Klasse mit statischen Methoden eignet. Wenn Sie eine (erfundene) Methode wie diese haben:

public static long Square(int x)
{
    return x * x;
}

dann ist dies hervorragend testbar und hat keine Nebenwirkungen. Sie überprüfen nur, ob Sie bei der Übergabe von beispielsweise 20 400 zurückbekommen. Kein Problem.

27
Dan Diplo

Wenn die eigentliche Frage hier lautet: "Wie teste ich diesen Code?":

public class MyClass
{
   public void MethodToTest()
   {
       //... do something
       MyStaticClass.StaticMethod();
       //...more
   }
}

Dann überarbeiten Sie einfach den Code und fügen den Aufruf der statischen Klasse wie gewohnt wie folgt ein:

public class MyClass
{
   private readonly IExecutor _externalExecutor;
   public MyClass(_IExecutor executor)
   {
       _exeternalExecutor = executor;
   }

   public void MethodToTest()
   {
       //... do something
       _exetrnalExecutor.DoWork();
       //...more
   }
}

public class MyStaticClassExecutor : IExecutor
{
    public void DoWork()
    {
        MyStaticClass.StaticMethod();
    }
}
18
Sunny

Statik ist nicht unbedingt böse, aber sie kann Ihre Möglichkeiten einschränken, wenn es um Unit-Tests mit Fakes/Mocks/Stubs geht.

Es gibt zwei allgemeine Ansätze zum Verspotten.

Die erste (traditionell - implementiert von RhinoMocks, Moq, NMock2; manuelle Mocks und Stubs befinden sich ebenfalls in diesem Lager) basiert auf Testnähten und Abhängigkeitsinjektion. Angenommen, Sie testen statischen Code in Einheiten und er hat Abhängigkeiten. Was in dem so entworfenen Code häufig vorkommt, ist, dass die Statik ihre eigenen Abhängigkeiten erstellt und Abhängigkeitsinversion Invertiert. Sie werden schnell feststellen, dass Sie keine verspotteten Schnittstellen in den zu testenden Code einfügen können, der auf diese Weise entwickelt wurde.

Die zweite (verspotten Sie alles - implementiert von TypeMock, JustMock und Moles) basiert auf .NETs Profiling API . Es kann jede Ihrer CIL-Anweisungen abfangen und einen Teil Ihres Codes durch eine Fälschung ersetzen. Auf diese Weise können TypeMock und andere Produkte in diesem Camp alles verspotten: Statik, versiegelte Klassen, private Methoden - Dinge, die nicht testbar sind.

Es gibt eine anhaltende Debatte zwischen zwei Denkschulen. Man sagt, folge SOLID-Prinzipien und entwerfe auf Testbarkeit (dazu gehört oft auch, die Statik zu schonen). Der andere sagt, kaufe TypeMock und mach dir keine Sorgen.

15
azheglov

Überprüfen Sie dies: "Statische Methoden sind der Tod der Testbarkeit" . Kurze Zusammenfassung des Arguments:

Zum Komponententest müssen Sie einen kleinen Teil Ihres Codes nehmen, seine Abhängigkeiten neu verkabeln und isoliert testen. Dies ist bei statischen Methoden schwierig, nicht nur, wenn sie auf den globalen Status zugreifen, sondern auch, wenn sie nur andere statische Methoden aufrufen.

14
Rafał Dowgird

Die einfache Wahrheit, die selten anerkannt wird, ist, dass eine Klasse, die eine vom Compiler sichtbare Abhängigkeit von einer anderen Klasse enthält, nicht isoliert von dieser Klasse getestet werden kann. Sie können etwas vortäuschen, das wie ein Test aussieht und in einem Bericht so angezeigt wird, als wäre es ein Test.

Es wird jedoch nicht die Schlüsseldefinitionseigenschaften eines Tests haben. scheitern, wenn die Dinge falsch sind, scheitern, wenn sie richtig sind.

Dies gilt für statische Aufrufe, Konstruktoraufrufe und Verweise auf Methoden oder Felder, die nicht von einer Basisklasse oder Schnittstelle geerbt wurden . Wenn der Klassenname im Code angezeigt wird, handelt es sich um eine vom Compiler sichtbare Abhängigkeit, ohne die Sie keinen gültigen Test durchführen können. Jeder kleinere Block ist einfach keine gültige testbare Einheit. Jeder Versuch, es so zu behandeln, als ob es wäre, führt zu Ergebnissen, die nicht aussagekräftiger sind, als ein kleines Dienstprogramm zu schreiben, mit dem das von Ihrem Testframework verwendete XML ausgegeben wird, um "Test bestanden" zu sagen.

Dafür gibt es drei Möglichkeiten:

  1. definieren Sie Unit-Tests als Tests der Einheit, die aus einer Klasse und ihren fest codierten Abhängigkeiten besteht. Dies funktioniert, sofern Sie zirkuläre Abhängigkeiten vermeiden.

  2. erstellen Sie niemals Abhängigkeiten zur Kompilierungszeit zwischen Klassen, für deren Test Sie verantwortlich sind. Dies funktioniert, vorausgesetzt, Sie haben nichts gegen den resultierenden Codestil.

  3. nicht Unit-Test, sondern Integrationstest. Was funktioniert, vorausgesetzt, es steht nicht im Widerspruch zu etwas anderem, für das Sie den Begriff Integrationstest verwenden müssen.

5
soru

Es gibt keine zwei Möglichkeiten. Die Vorschläge von ReSharper und einige nützliche Funktionen von C # würden nicht so oft verwendet, wenn Sie isolierte Atom-Unit-Tests für Ihren gesamten Code schreiben würden.

Wenn Sie beispielsweise über eine statische Methode verfügen und diese löschen müssen, können Sie dies nur tun, wenn Sie ein profilbasiertes Isolationsframework verwenden. Eine aufrufkompatible Problemumgehung besteht darin, den oberen Rand der Methode so zu ändern, dass die Lambda-Notation verwendet wird. Zum Beispiel:

VOR:

    public static DBConnection ConnectToDB( string dbName, string connectionInfo ) {
    }

NACH:

    public static Func<string, string, DBConnection> ConnectToDB (dbName, connectionInfo ) {
    };

Die beiden sind anrufkompatibel. Anrufer müssen sich nicht ändern. Der Funktionskörper bleibt gleich.

Dann können Sie diesen Aufruf in Ihrem Unit-Test-Code wie folgt stubben (vorausgesetzt, er gehört zu einer Klasse namens Database):

        Database.ConnectToDB = (dbName, connectionInfo) => { return null|whatever; }

Achten Sie darauf, es durch den ursprünglichen Wert zu ersetzen, nachdem Sie fertig sind. Sie können dies über einen Versuch/Endlich tun oder in Ihrer Unit-Test-Bereinigung den Code, der nach jedem Test aufgerufen wird, wie folgt schreiben:

    [TestCleanup]
    public void Cleanup()
    {
        typeof(Database).TypeInitializer.Invoke(null, null);
    }

dadurch wird der statische Initialisierer Ihrer Klasse erneut aufgerufen.

Lambda Funcs sind nicht so unterstützungsreich wie normale statische Methoden, daher hat dieser Ansatz die folgenden unerwünschten Nebenwirkungen:

  1. Wenn die statische Methode eine Erweiterungsmethode war, müssen Sie sie zuerst in eine Nichterweiterungsmethode ändern. Resharper kann dies automatisch für Sie erledigen.
  2. Wenn einer der Datentypen der statischen Methoden eine eingebettete Interop-Assembly ist, z. B. für Office, müssen Sie die Methode umbrechen, den Typ umbrechen oder in den Typ 'Objekt' ändern.
  3. Sie können das Refactoring-Tool für Änderungssignaturen von Resharper nicht mehr verwenden.

Angenommen, Sie vermeiden Statik vollständig und konvertieren diese in eine Instanzmethode. Es ist immer noch nicht verspottbar, es sei denn, die Methode ist entweder virtuell oder als Teil einer Schnittstelle implementiert.

In Wirklichkeit ist jeder, der vorschlägt, statische Methoden zu stoppen, darin, sie zu Instanzmethoden zu machen. Sie wären auch gegen Instanzmethoden, die nicht virtuell oder Teil einer Schnittstelle sind.

Warum hat C # statische Methoden? Warum sind nicht virtuelle Instanzmethoden zulässig?

Wenn Sie eine dieser "Funktionen" verwenden, können Sie einfach keine isolierten Methoden erstellen.

Wann benutzt du sie?

Verwenden Sie sie für jeden Code, von dem Sie nicht erwarten, dass jemand ihn jemals auslöschen möchte. Einige Beispiele: die Format () -Methode der String-Klasse, die WriteLine () -Methode der Console-Klasse, die Cosh () -Methode der Math-Klasse

Und noch etwas. Die meisten Leute kümmern sich nicht darum, aber wenn Sie die Leistung eines indirekten Aufrufs beurteilen können, ist dies ein weiterer Grund, Instanzmethoden zu vermeiden. Es gibt Fälle, in denen es sich um einen Leistungseinbruch handelt. Deshalb gibt es überhaupt nicht virtuelle Methoden.

4
zumalifeguard

Ich sehe, dass nach langer Zeit noch niemand eine wirklich einfache Tatsache festgestellt hat. Wenn Resharper mir sagt, dass ich eine Methode statisch machen kann, bedeutet das eine große Sache für mich. Ich kann hören, wie seine Stimme mir sagt: "Hey, Sie, diese logischen Elemente sind nicht die VERANTWORTUNG der aktuellen Klasse, also sollte sie draußen bleiben in einer Helferklasse oder so ".

3
g1ga
  1. Ich glaube, das liegt zum Teil daran, dass statische Methoden "schneller" aufzurufen sind als Instanzmethoden. (In Anführungszeichen, weil dies nach Mikrooptimierung riecht) siehe http://dotnetperls.com/static-method
  2. Es sagt Ihnen, dass es keinen Status benötigt, daher von überall aufgerufen werden kann, wodurch der Instatiation-Overhead beseitigt wird, wenn dies das einzige ist, was jemand benötigt.
  3. Wenn ich es verspotten will, dann denke ich, ist es im Allgemeinen die Praxis, dass es auf einer Schnittstelle deklariert ist.
  4. Wenn es auf einer Schnittstelle deklariert ist, schlägt R # nicht vor, dass Sie es statisch machen.
  5. Wenn es als virtuell deklariert ist, schlägt R # auch nicht vor, es statisch zu machen.
  6. Das statische Halten von Status (Feldern) sollte immer sorgfältig geprüft werden . Statischer Zustand und Fäden vermischen sich wie Lithium und Wasser.

R # ist nicht das einzige Tool, das diesen Vorschlag macht. Die FxCop/MS-Code-Analyse wird dasselbe tun.

Ich würde allgemein sagen, dass wenn die Methode statisch ist, sie im Allgemeinen auch so wie sie ist testbar sein sollte. Das bringt einige Designüberlegungen und wahrscheinlich mehr Diskussionen mit sich, als ich gerade in meinen Fingern habe, also warte geduldig auf die Abstimmungen und Kommentare ...;)

3
MIA

Wenn die statische Methode von innerhalb einer anderen Methode aufgerufen wird, ist es nicht möglich, einen solchen Aufruf zu verhindern oder zu ersetzen. Dies bedeutet, dass diese beiden Methoden eine einzige Einheit bilden. Unit Test jeglicher Art testet beide.

Und wenn diese statische Methode mit dem Internet kommuniziert, Datenbanken verbindet, GUI-Popups anzeigt oder den Unit-Test auf andere Weise in völliges Chaos umwandelt, ist dies einfach ohne einfache Umgehung möglich. Eine Methode, die eine solche statische Methode aufruft, kann ohne Refactoring nicht getestet werden, selbst wenn sie viel rein rechnerischen Code enthält, der von einem Unit-Test stark profitieren würde.

2
h22

Ich glaube, dass Resharper Ihnen Hinweise gibt und die Codierungsrichtlinien anwendet, mit denen es eingerichtet wurde. Wenn ich Resharper verwendet habe und mir mitgeteilt wurde, dass eine Methode statisch sein sollte, muss sie sich auf eine private Methode beziehen, die auf keine Instanzvariablen einwirkt.

Was die Testbarkeit betrifft, sollte dieses Szenario kein Problem darstellen, da Sie private Methoden sowieso nicht testen sollten.

In Bezug auf die Testbarkeit von statischen Methoden, die öffentlich sind, wird das Testen von Einheiten schwierig, wenn statische Methoden den statischen Zustand berühren. Persönlich würde ich dies auf ein Minimum beschränken und statische Methoden so weit wie möglich als reine Funktionen verwenden, wenn Abhängigkeiten an die Methode übergeben werden, die über eine Testvorrichtung gesteuert werden können. Dies ist jedoch eine Entwurfsentscheidung.

0
aqwert