Mock Framework gegen MS Fakes Frameworks

99

Ein bisschen verwirrt über die Unterschiede zwischen Mock-Frameworks wie NMock und dem VS 2011 Fakes Framework. Wenn ich MSDN durchlaufe, verstehe ich, dass Sie mit Fakes Ihre Abhängigkeiten wie RhinoMock oder NMock verspotten können. Der Ansatz ist jedoch anders. Fakes generiert Code, um diese Funktionalität zu erreichen, das Mocks-Framework jedoch nicht. Ist mein Verständnis also richtig? Is Fakes ist nur ein weiteres Mock-Framework

Nairooz NIlafdeen
quelle

Antworten:

189

Ihre Frage bezog sich darauf, wie sich das MS Fakes-Framework von NMock unterscheidet, und es scheint, dass die anderen Antworten einige davon gelöst haben. Hier finden Sie jedoch weitere Informationen dazu, wie sie gleich und wie unterschiedlich sie sind. NMock ähnelt auch RhinoMocks und Moq, daher gruppiere ich sie mit NMock.

Es gibt drei Hauptunterschiede zwischen NMock / RhinoMocks / Moq und dem MS Fakes Framework:

  • Das MS Fakes Framework verwendet generierten Code, ähnlich wie Accessors in früheren Versionen von Visual Studio anstelle von generischen Typen. Wenn Sie das Fakes-Framework für eine Abhängigkeit verwenden möchten, fügen Sie die Assembly, die die Abhängigkeit enthält, zu den Referenzen des Testprojekts hinzu und klicken Sie mit der rechten Maustaste darauf, um die Test-Doubles (Stubs oder Shims) zu generieren. Wenn Sie dann testen, verwenden Sie stattdessen tatsächlich diese generierten Klassen. NMock verwendet Generika, um dasselbe zu erreichen (dh IStudentRepository studentRepository = mocks.NewMock<IStudentRepository>()). Meiner Meinung nach verhindert der MS Fakes-Framework-Ansatz die Code-Navigation und das Refactoring innerhalb der Tests, da Sie tatsächlich gegen eine generierte Klasse arbeiten, nicht gegen Ihre eigentliche Schnittstelle.

  • Das MS Fakes Framework liefert Stubs und Moles (Shims), während NMock, RhinoMocks und Moq Stubs und Mocks liefern . Ich verstehe die Entscheidung von MS, keine Verspottungen aufzunehmen, wirklich nicht und bin persönlich aus den unten beschriebenen Gründen kein Fan von Maulwürfen.

  • Mit dem MS Fakes Framework bieten Sie eine alternative Implementierung der Methoden, die Sie stubben möchten. In diesen alternativen Implementierungen können Sie die Rückgabewerte angeben und Informationen darüber verfolgen, wie oder ob die Methode aufgerufen wurde. Mit NMock, RhinoMocks und Moq generieren Sie ein Scheinobjekt und verwenden dieses Objekt dann, um Stubbed-Rückgabewerte anzugeben oder Interaktionen zu verfolgen (ob und wie die Methoden aufgerufen wurden). Ich finde den Ansatz von MS-Fälschungen komplexer und weniger ausdrucksstark.

Um den Unterschied in den Frameworks zu verdeutlichen: NMock, RhinoMocks und Moq bieten zwei Arten von Test-Doubles (Stubs und Mocks). Das Fakes-Framework bietet Stubs und Moles (sie nennen sie Shims) und enthält leider keine Mocks. Um die Unterschiede und Ähnlichkeiten zwischen NMock und MS Fakes zu verstehen, ist es hilfreich zu verstehen, was diese verschiedenen Arten von Testdoppeln sind:

Stubs: Stubs werden verwendet, wenn Sie Werte für Methoden oder Eigenschaften angeben müssen, die von der zu testenden Methode von Ihrem Testdoppel verlangt werden. Wenn meine zu testende Methode beispielsweise die DoesStudentExist () -Methode des IStudentRepository-Testdoppels aufruft, möchte ich, dass sie true zurückgibt.

Die Idee von Stubs in NMock- und MS-Fälschungen ist dieselbe, aber mit NMock würden Sie so etwas tun:

Stub.On(mockStudentRepository).Method("DoesStudentExist").Will(Return.Value(true));

Und mit MSFakes würden Sie so etwas tun:

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() // Generated by Fakes.
{
    DoesStudentExistInt32 = (studentId) => { return new Student(); }
};

Beachten Sie, dass Sie im MS Fakes-Beispiel eine völlig neue Implementierung für die DoesStudentExist-Methode erstellen (Beachten Sie, dass sie DoesStudentExistInt32 heißt, da das Fakes-Framework die Parameterdatentypen an die Methodennamen anfügt, wenn es die Stub-Objekte generiert. Ich denke, dies beeinträchtigt die Klarheit von Die Tests). Um ehrlich zu sein, nervt mich die NMock-Implementierung auch, weil sie eine Zeichenfolge verwendet, um den Methodennamen zu identifizieren. (Verzeihen Sie mir, wenn ich falsch verstanden habe, wie NMock verwendet werden soll.) Dieser Ansatz hemmt das Refactoring wirklich und ich würde RhinoMocks oder Moq NMock aus diesem Grund wärmstens empfehlen.

Mocks: Mocks werden verwendet, um die Interaktion zwischen Ihrer zu testenden Methode und ihren Abhängigkeiten zu überprüfen. Mit NMock tun Sie dies, indem Sie ähnliche Erwartungen festlegen:

Expect.Once.On(mockStudentRepository).Method("Find").With(123);

Dies ist ein weiterer Grund, warum ich RhinoMocks und Moq NMock vorziehen würde. NMock verwendet den älteren Erwartungsstil, während RhinoMocks und Moq beide den Arrange / Act / Assert-Ansatz unterstützen, bei dem Sie Ihre erwarteten Interaktionen als Zusicherungen am Ende des Tests wie diesen angeben ::

stubStudentRepository.AssertWasCalled( x => x.Find(123));

Beachten Sie erneut, dass RhinoMocks ein Lambda anstelle einer Zeichenfolge verwendet, um die Methode zu identifizieren. Das ms fakes-Framework bietet überhaupt keine Mocks. Dies bedeutet, dass Sie in Ihren ausgeblendeten Implementierungen (siehe Beschreibung der Stubs oben) Variablen festlegen müssen, die Sie später überprüfen, ob sie korrekt festgelegt wurden. Das würde ungefähr so ​​aussehen:

bool wasFindCalled = false;

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() 
{
    DoesStudentExistInt32 = (studentId) => 
        { 
            wasFindCalled = true;
            return new Student(); 
        }
};

classUnderTest.MethodUnderTest();

Assert.IsTrue(wasFindCalled);

Ich finde diesen Ansatz etwas verworren, da Sie den Anruf im Stub verfolgen und später im Test bestätigen müssen. Ich finde die Beispiele für NMock und insbesondere für RhinoMocks ausdrucksstärker.

Maulwürfe (Shims): Um ehrlich zu sein, ich mag Maulwürfe nicht, weil sie möglicherweise missbraucht werden. Eines der Dinge, die ich an Unit-Tests (und insbesondere an TDD) so sehr mag, ist, dass das Testen Ihres Codes Ihnen hilft, zu verstehen, wo Sie schlechten Code geschrieben haben. Dies liegt daran, dass das Testen von schlecht geschriebenem Code schwierig ist. Dies gilt nicht für die Verwendung von Maulwürfen, da Maulwürfe tatsächlich so konzipiert sind, dass Sie gegen nicht injizierte Abhängigkeiten testen oder private Methoden testen können. Sie funktionieren ähnlich wie Stubs, außer dass Sie einen ShimsContext wie folgt verwenden:

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(fixedYear, 1, 1); };
}

Meine Sorge bei Unterlegscheiben ist, dass die Leute sie als "einfacheren Weg zum Unit-Test" ansehen, weil Sie dadurch nicht gezwungen werden, Code so zu schreiben, wie Sie es sollten. Eine ausführlichere Beschreibung dieses Konzepts finden Sie in meinem Beitrag:

Weitere Informationen zu einigen Bedenken im Zusammenhang mit den gefälschten Frameworks finden Sie in den folgenden Beiträgen:

Wenn Sie RhinoMocks lernen möchten, finden Sie hier ein Pluralsight-Schulungsvideo (vollständige Offenlegung - Ich habe diesen Kurs geschrieben und bekomme Lizenzgebühren für Ansichten, aber ich denke, dass dies für diese Diskussion gilt, also füge ich ihn hier ein):

Jim Cooper
quelle
14
@ Jim Ich bin nicht der Meinung, dass es eine schlechte Sache ist, zuzulassen, dass man "auf Abhängigkeiten testet, die nicht injiziert werden". Im Gegenteil, in der Praxis hilft diese Fähigkeit zu vermeiden, dass eine Codebasis mit vielen sinnlosen Schnittstellen und der damit verbundenen Konfiguration / Komplexität der "Objektverdrahtung" überladen wird. Ich habe bereits einige Projekte gesehen (sowohl Java als auch .NET), die Hunderte von Java / C # -Schnittstellen mit nur einer einzigen implementierenden Klasse und viele nutzlose XML-Konfigurationen (für Spring DI-Beans oder in Web.config für MS Unity) hatten Rahmen). Solche Abhängigkeiten werden besser nicht injiziert.
Rogério
19
@Rogerio, ich habe an mehreren Projekten mit Tausenden von Klassen gearbeitet, die diesem Modell folgen, und wenn die Abhängigkeitsinjektion richtig durchgeführt wird (wenn Sie die XML-Konfiguration verwenden, machen Sie es nicht richtig, imho), ist es einfach und relativ schmerzlos. Andererseits habe ich an Systemen gearbeitet, die dies nicht tun, und die Kopplung zwischen Klassen hat mir immer erhebliche Schmerzen bereitet. Testen ist nicht der Grund für die Verwendung der Abhängigkeitsinjektion (obwohl dies kein schlechter Grund ist). Der wahre Grund ist die gelöste Kupplung. Schnittstellen zu haben, die von einer einzelnen Klasse implementiert werden, hat mir nie Schmerzen bereitet.
Jim Cooper
17
@ Jim, ich sehe die Fähigkeit, schlechtes Design zu testen, als großes Plus. Das erste, was Sie tun möchten, bevor Sie Legacy-Code in Richtung eines besseren Designs umgestalten, sind Schreibtests.
Thomas Materna
4
Meine Regel ist, dass ich Fakes nur verwende, wenn ich eine gemeinsam genutzte / statische Methode oder eine Klasse Shim muss, auf die ich keinen Zugriff habe, um eine Schnittstelle zu implementieren. Für alles andere benutze ich Moq. Fälschungen sind langsamer (da die gefälschten Baugruppen generiert werden müssen), und es erfordert mehr Codierungsaufwand, um die mit Moq erhaltenen Funktionen anzupassen.
Nick
5
@ CarloV.Dango Zunächst einmal, ich weiß ganz genau , dass Dependency Injection ist nicht separate Schnittstellen erfordern; Mein vorheriger Kommentar spielte nur auf die Tendenz an, solche Schnittstellen bei Verwendung von DI zu erstellen. Zweitens hat "IOC" (Inversion of Control) nichts mit DI zu tun ! (Bei der ersten geht es um die Umkehrung des Kontrollflusses zwischen Methoden, bei der zweiten um die Auflösung von Abhängigkeiten für eine Komponente / ein Objekt.) Durch die Verwechslung von IOC und DI zeigen Sie Unwissenheit, nicht ich. und ehrlich gesagt verdient diese Seite nicht, dass Leute andere beleidigen, also lasst es uns bitte zivil halten.
Rogério
23

Sie haben Recht, aber die Geschichte enthält noch mehr. Die wichtigsten Dinge, die Sie aus dieser Antwort herausnehmen sollten, sind:

  1. Ihre Architektur sollte Stubs und Abhängigkeitsinjektion richtig nutzen, anstatt sich auf die Krücke von Fakes und Mocks zu verlassen

  2. Fälschungen und Verspottungen sind nützlich, um Code zu testen, den Sie nicht ändern sollten oder können, z.

    • Legacy-Code, der Stubs nicht nutzt (oder effizient nutzt)
    • APIs von Drittanbietern
    • Ressourcen, für die Sie keinen Quellcode haben

Shims (während der Entwicklung als "Maulwürfe" bekannt) sind in der Tat ein spöttisches Framework, das als Umleitung von Anrufen dient. Anstatt akribisch ein Modell zu erstellen (ja, selbst die Verwendung von Moq ist relativ schmerzhaft!), Verwenden Shims einfach das bereits vorhandene Produktionscode-Objekt. Shims leiten den Anruf einfach vom Produktionsziel an den Testdelegierten weiter.

Stubs werden aus Schnittstellen im Zielprojekt generiert. Das Stub-Objekt ist genau das - eine Implementierung der Schnittstelle. Der Vorteil der Verwendung des Stub-Typs besteht darin, dass Sie schnell einen Stub generieren können, ohne Ihr Testprojekt mit vielen Stubs zur einmaligen Verwendung zu überladen, ganz zu schweigen von der Zeitverschwendung beim Erstellen. Natürlich sollten Sie immer noch konkrete Stichleitungen erstellen, die für viele Tests verwendet werden können.

Die effiziente Implementierung von Fakes (Shims, Mocks und Stub-Typen) ist etwas gewöhnungsbedürftig, aber die Mühe lohnt sich. Ich persönlich habe durch die Verwendung der Typen Shims / Mole, Mocks und Stub Wochen Entwicklungszeit gespart. Ich hoffe, Sie haben genauso viel Spaß mit der Technologie wie ich!

Mike Christian
quelle
11
Aufgrund fehlender Details und einiger terminologischer Probleme mit Ihren Kommentaren zu Fakes herabgestimmt. Fakes ist ein allgemeiner Begriff für alle Arten von Test-Doubles und auch der Name MS, der für ihre Fakes-Bibliothek verwendet wird. In ihrer Bibliothek sind nur Shims Maulwürfe, Stubs nicht. In Bezug auf die Notwendigkeit von Beispielen würde ich gerne ein Beispiel in Ihrer Antwort sehen, das zeigt, wie das Erstellen eines Shims (oder Stubs) mit Fakes einfacher ist als die Verwendung von Moq. Wenn Sie Änderungen an Ihrer Antwort vornehmen, um diese zu beheben, ändere ich meine Ablehnung.
Jim Cooper
1
Fügt jedoch eine gute Rechtfertigung für die Verwendung von Shims (Maulwürfen) hinzu, dh Code von Drittanbietern, System- / SDK-APIs. Es geht nicht nur darum, wann Sie mit Ihren eigenen internen Lösungen arbeiten. Also werde ich dir eine Stimme geben,
Tony Wall
2
@ Mike und Jim .. Ich habe mich bemüht, die in dieser Antwort verwendete Terminologie zu korrigieren. Bitte lassen Sie mich wissen, ob wir es besser machen können. Vielen Dank.
Snesh
15

So wie ich es verstehe, wollte das Visual Studio-Team vermeiden, mit den verschiedenen für .NET verfügbaren Scheinbibliotheken zu konkurrieren. MS steht oft vor solchen schwierigen Entscheidungen. Sie sind aufgestaut, wenn sie bestimmte Funktionen nicht bereitstellen ("Warum stellt MS uns keine Scheinbibliothek zur Verfügung; Verspottungen sind eine so häufige Anforderung?") Und verdammt, wenn sie dies tun ("Warum handelt Microsoft so aggressiv und treibt seine an) natürliche Unterstützer aus dem Markt? ") Sehr oft, aber nicht immer, beschließen sie, sich zurückzuhalten, einfach ihre eigene Alternative zu verfügbaren und gut aufgenommenen Technologien anzubieten. Das scheint hier der Fall zu sein.

Die Shim-Funktion von Fakes ist wirklich sehr, sehr nützlich. Klar, es gibt Gefahren. Es erfordert etwas Disziplin, um sicherzustellen, dass Sie dies nur bei Bedarf verwenden. Es füllt jedoch eine große Lücke. Meine Hauptbeschwerde ist, dass es nur mit der Ultimate Edition von VS 2012 geliefert wird und daher nur einem Unterabschnitt der .NET-Entwicklergemeinschaft zur Verfügung steht. Was für eine Schande.

Charles Young
quelle
2
Das Fakes-Framework ist auch mit der Premium Edition verfügbar.
AlwaysAProgrammer
13

Fakes enthält zwei verschiedene Arten von "gefälschten" Objekten. Der erste, der als "Stub" bezeichnet wird, ist im Wesentlichen ein automatisch generierter Dummy, dessen Standardverhalten überschrieben werden kann (und normalerweise würde), um ihn zu einem interessanteren Mock zu machen. Es fehlen jedoch einige der Funktionen, die die meisten derzeit verfügbaren Mocking-Frameworks bieten. Wenn Sie beispielsweise überprüfen möchten, ob eine Methode für eine Stub-Instanz aufgerufen wurde, müssen Sie die Logik dafür selbst hinzufügen. Wenn Sie Ihre eigenen Mocks jetzt manuell erstellen, scheinen Stubs im Grunde genommen eine Verbesserung zu sein. Wenn Sie jedoch bereits ein umfassenderes Mocking-Framework verwenden, haben Sie möglicherweise das Gefühl, dass einige wichtige Teile in Fakes-Stubs fehlen.

Die andere von Fakes angebotene Objektkategorie, die als "Shim" bezeichnet wird, stellt einen Mechanismus zum Ersetzen des Verhaltens von Abhängigkeiten bereit, die für den Standardersatz über Mocks nicht angemessen entkoppelt wurden (oder nicht entkoppelt werden können). AFAIK, TypeMock ist das einzige der wichtigsten Verspottungs-Frameworks, das derzeit diese Art von Funktionalität bietet.

Übrigens, wenn Sie Moles schon einmal ausprobiert haben, ist Fakes im Wesentlichen dasselbe und findet schließlich seinen Weg aus Microsoft Research in ein tatsächliches Produkt.

Nicole Calinoiu
quelle
1
Update: Moles wurde jetzt in MS Fakes integriert. "Das Fakes-Framework in Visual Studio 2012 ist die nächste Generation von Moles & Stubs. Fakes unterscheiden sich jedoch von Moles. Für den Wechsel von Moles zu Fakes sind daher einige Änderungen an Ihrem Code erforderlich. Das Moles-Framework wird in Visual Studio 2012 nicht unterstützt . " Quelle: research.microsoft.com/en-us/projects/moles
Sire
1

In Bezug auf gefälschte (Shim + Stub) Objekte wurde es oben gut definiert, obwohl ich denke, dass der letzte Absatz im letzten Kommentar die gesamte Situation ziemlich gut zusammenfasst.

Obwohl viele Leute argumentieren werden, dass gefälschte (Shim + Stub) Objekte in einigen Unit-Test-Fällen ein gutes Gut sind, ist der Nachteil, dass diese Optionen NUR verfügbar sind, egal ob Sie Visual Studio 2012 oder Visual Studio 2013 verwenden mit Premium- oder Ultimate-Versionen. IOW, dies bedeutet, dass Sie KEINE dieser Fälschungen (Shim + Stub) auf einer Pro-Version ausführen dürfen.

Möglicherweise wird die Menüoption "Fälschungen" (Shim + Stub) in Pro-Versionen angezeigt, aber Vorsicht, es besteht eine ziemlich hohe Wahrscheinlichkeit, dass Sie am Ende absolut nichts haben ... Es wird kein Kompilierungsfehler generiert, der Ihnen sagt, dass etwas Wichtiges vorliegt fehlt, Optionen sind einfach nicht da, also verschwenden Sie nicht Ihre Zeit ...

Dies ist ein wichtiger Faktor, der in einem Entwicklerteam berücksichtigt werden muss, insbesondere wenn einer der einzigen ist, der die Ultimate-Version verwendet, während alle anderen die Pro-Version verwenden. Moq hingegen kann problemlos über Nuget installiert werden, unabhängig davon, welche Visual Studio-Version Sie verwenden. Ich hatte kein Problem mit Moq, der Schlüssel bei jedem Tool ist zu wissen, wofür sie verwendet werden und wie man sie richtig verwendet;)

SirXamelot
quelle
1
Bitte formatieren Sie Ihre Frage in kleinere Absätze, damit sie leichter zu lesen ist.
@ SirXamelot, ohne den Code umzugestalten, können Sie die Ausgabe von .net nicht ändern, sofern statische Aufrufe, z. B. DateTime.Now oder Guid.NewGuid
zaitsman