2008-08-09 12 views
55

Mocking sealed classes kann ziemlich schmerzhaft sein. Ich bevorzuge derzeit eine Adapter pattern, um damit umzugehen, aber etwas über nur hält sich komisch an.Wie verspotten Sie eine Sealed-Klasse?

Also, was ist der beste Weg, versiegelte Klassen zu verspotten?

Java Antworten sind mehr als willkommen. In der Tat würde ich erwarten, dass die Java-Community damit länger beschäftigt ist und viel zu bieten hat.

Aber hier sind einige der .NET-Meinungen:

+0

Nicht verspotten! [Verwenden Sie stattdessen die Komposition] (http://stackoverflow.com/a/891820/274502). – cregox

Antwort

6

Meine Faustregel ist, dass Objekte, die ich verspotten müssen auch eine gemeinsame Schnittstelle haben sollte. Ich denke, das ist richtig Design-weise und macht Tests viel einfacher (und ist in der Regel, was Sie bekommen, wenn Sie TDD tun). Weitere Informationen hierzu finden Sie im Google Testing Blog latest post (siehe Punkt 9).

Auch habe ich in den letzten 4 Jahren hauptsächlich in Java gearbeitet und ich kann sagen, dass ich an einer Hand zählen kann, wie oft ich eine endgültige (versiegelte) Klasse erstellt habe. Eine andere Regel ist, dass ich immer einen guten Grund haben sollte, eine Klasse zu versiegeln, anstatt sie standardmäßig zu versiegeln.

+19

Ich würde argumentieren, dass Sie einen guten Grund haben sollten * nicht * eine Klasse zu versiegeln. Wenn Sie eine Klasse offen lassen, müssen Sie darüber nachdenken, wie sie von den Erben verwendet wird. Dies eröffnet eine Flut von Entscheidungen über den gesamten Code in der Klasse (Virtualität, geschützte Eigenschaften oder private Membervariablen usw.) Es ist * schwer * eine Klasse für die Vererbung richtig entwerfen. Sie sollten nicht in der Lage sein, eine Klasse nur zur Erweiterung zu erweitern; Derivation sollte etwas Spezifisches für das zu modellierende Problem bedeuten. Ansonsten bevorzugen Sie stattdessen die Komposition. –

+2

Bryan - wie würdest du dann eine versiegelte Klasse verhöhnen? Ich stimme Ihnen aus theoretischer Sicht zu, aber wenn Sie etwas schreiben, das von Ihrer versiegelten Klasse abhängt, dann hängen Ihre Tests auch von der versiegelten Klasse ab. –

+4

@abyx: Leider ist es nicht immer die Entscheidung des Entwicklers, ob eine Klasse versiegelt ist oder nicht. Nehmen wir zum Beispiel System.Web.HttpServerUtility in ASP.NET ... – chiccodoro

1

Gibt es eine Möglichkeit, eine versiegelte Klasse von einer Schnittstelle zu implementieren ... und verspotten Sie stattdessen die Schnittstelle?

Etwas in mir fühlt, dass versiegelte Klassen mit an erster Stelle falsch ist, aber das ist nur mir :)

+2

Ich denke, versiegelte Klassen sind großartig ... das Problem ist das Testen. Es stinkt, dass wir aufgrund von Einschränkungen beim Testen von .NET-Projekten unser gesamtes Design ändern müssen. In vielen Fällen D.I. ist einfach ein Weg, um die Tatsache zu umgehen, dass statische Klassen/Methoden schwer zu testen sind. –

18

Für .NET, könnten Sie etwas wie TypeMock verwenden, die die Profiling-API verwendet und ermöglicht es Ihnen, Anrufe an fast alles zu haken.

+7

+1. Verwenden Sie die richtigen Werkzeuge. Lass dich nicht von Werkzeugen diktieren, wie du Dinge tun sollst. APIs sind für Leute, nicht für Werkzeuge - entwerfen Sie sie als solche. Verwenden Sie DI und Schnittstellen und vollständige Entkopplung, wo es sinnvoll ist, nicht nur, weil Sie es zum Testen von Tools benötigen. –

+2

Pavel - das Problem ist, dass der "richtige" Weg in .NET führt Sie normalerweise einen Pfad von "Verwenden Sie TypeMock zu testen". Einem Pfad zu folgen, auf dem nur ein Testwerkzeug verfügbar ist, scheint auch nicht gut zu sein. –

+5

Manche Leute möchten vielleicht nicht 80 $ pro Monat für ein Testframework bezahlen. –

4

Das Problem mit TypeMock ist, dass es schlechtes Design entschuldigt. Jetzt weiß ich, dass es oft jemand anderes schlechtes Design ist, dass es sich versteckt, aber es in Ihren Entwicklungsprozess zuzulassen kann sehr leicht dazu führen, Ihre eigenen schlechten Designs zu erlauben.

Ich denke, wenn Sie ein Mocking-Framework verwenden, sollten Sie eine traditionelle (wie Moq) und erstellen Sie eine Isolationsschicht um die nicht abkoppelbare Sache, und verspotten Sie stattdessen die Isolationsschicht.

+11

Nicht slapping Schnittstellen auf alles in Sicht, nur weil Sie sie wegen der Mängel Ihrer Test-Tools brauchen, ist nicht schlechtes Design. Im Gegenteil, es ist _sane_ Design, bei dem es um Design geht, nicht darum, sich an Ihre Werkzeuge anzupassen. Ich schwöre, manchmal denke ich, dass TDD, wie es oft dogmatisch praktiziert wird, wirklich "tools-driven design" genannt werden sollte. Siehe auch http://weblogs.asp.net/rosherove/archive/2008/01/17/is-typemock-too-powerful-will-it-kill-design-for-testability.aspx –

+0

Pavel - haben Sie ein anderes Tool als TypeMock, das diese Art von Tests bieten kann? Das Testen von Klassen, die mit vernünftigen Designs erstellt wurden (z. B. unter Verwendung von statischen Verfahren, neuen Aufrufen in Code, Beschränken von Schnittstellen, wo mehrere Implementierungen benötigt werden, usw.), sind oft nahezu unmöglich zu testen. –

+1

Ich stimme Brad hier zu. Das Erstellen eines Mock-Objekts bedeutet, dass Sie das öffentliche Verhalten dieses Typs testen. Das heißt, Sie sagen ausdrücklich: "Ich brauche die öffentliche API dieses Objekts, um sich auf eine bestimmte Weise zu verhalten." Dieses Verhalten ist * unabhängig *, wie auch immer diese API implementiert ist. Dies bedeutet, dass Sie eine bereits definierte (wenn auch implizite) Abstraktion haben, die sich auf eine bestimmte Art und Weise verhalten muss. In einem statischen System muss dies durch Erstellen eines abstrakten Typs explizit gemacht werden. –

1

Ich gehe im Allgemeinen den Weg der Erstellung einer Schnittstelle und Adapter/Proxy-Klasse, um das Mocking des versiegelten Typs zu erleichtern. Ich experimentierte jedoch auch damit, die Erstellung der Schnittstelle zu überspringen und den Proxy-Typ mit virtuellen Methoden versiegeln zu lassen. Dies funktionierte gut, wenn der Proxy tatsächlich eine natürliche Basisklasse ist, die einen Teil der versiegelten Klasse einkapselt und Benutzer enthält.

Beim Umgang mit Code, der diese Anpassung erforderte, wurde ich müde, die gleichen Aktionen auszuführen, um die Schnittstelle und den Proxy-Typ zu erstellen, also implementierte ich eine Bibliothek, um die Aufgabe zu automatisieren.

Der Code ist etwas ausgefeilter als das Beispiel in dem Artikel, auf den Sie verweisen, da er eine Assembly (anstelle von Quellcode) erzeugt, die Codegenerierung für jeden Typ ermöglicht und nicht so viel erfordert Aufbau.

Weitere Informationen finden Sie unter this page.

4

Ich habe fast immer vermeiden, Abhängigkeiten zu externen Klassen tief in meinem Code. Stattdessen würde ich lieber einen Adapter/eine Brücke verwenden, um mit ihnen zu sprechen. Auf diese Weise beschäftige ich mich mit meiner Semantik, und der Schmerz des Übersetzens ist in einer Klasse isoliert.

Es macht es auch einfacher, meine Abhängigkeiten auf lange Sicht zu wechseln.

1

Es ist durchaus sinnvoll, eine versiegelte Klasse zu verspotten, da viele Framework-Klassen versiegelt sind.

In meinem Fall versuche ich .Net MessageQueue-Klasse zu verspotten, so dass ich meine anmutige Ausnahmebehandlungslogik TDD.

Wenn jemand Ideen hat, wie Moq's Fehler in Bezug auf "Ungültiges Setup auf einem nicht überschreibbaren Mitglied" zu überwinden, lass es mich wissen.

Code:

[TestMethod] 
    public void Test() 
    { 
     Queue<Message> messages = new Queue<Message>(); 
     Action<Message> sendDelegate = msg => messages.Enqueue(msg); 
     Func<TimeSpan, MessageQueueTransaction, Message> receiveDelegate = 
      (v1, v2) => 
      { 
       throw new Exception("Test Exception to simulate a failed queue read."); 
      }; 

     MessageQueue mockQueue = QueueMonitorHelper.MockQueue(sendDelegate, receiveDelegate).Object; 
    } 
    public static Mock<MessageQueue> MockQueue 
       (Action<Message> sendDelegate, Func<TimeSpan, MessageQueueTransaction, Message> receiveDelegate) 
    { 
     Mock<MessageQueue> mockQueue = new Mock<MessageQueue>(MockBehavior.Strict); 

     Expression<Action<MessageQueue>> sendMock = (msmq) => msmq.Send(It.IsAny<Message>()); //message => messages.Enqueue(message); 
     mockQueue.Setup(sendMock).Callback<Message>(sendDelegate); 

     Expression<Func<MessageQueue, Message>> receiveMock = (msmq) => msmq.Receive(It.IsAny<TimeSpan>(), It.IsAny<MessageQueueTransaction>()); 
     mockQueue.Setup(receiveMock).Returns<TimeSpan, MessageQueueTransaction>(receiveDelegate); 

     return mockQueue; 
    } 
+0

Ich habe eine Lösung gefunden und hier veröffentlicht: http://www.dotnetmonkey.net/post/Hard-to-Moq.aspx –

13

Ich glaube, dass Moles, von Microsoft Research, Sie das tun können. Von der Seite Moles:

Moles verwendet werden, kann jede Methode .NET Umweg, einschließlich nicht-virtuelle/static Methoden in versiegelten Typen.

UPDATE: gibt es einen neuen Rahmen "Fälschungen" in der anstehenden VS 11 Release aufgerufen, die entworfen ist Moles ersetzt werden:

Die Fakes Framework in Visual Studio 11 ist die nächste Generation von Moles & Stubs und wird es schließlich ersetzen. Fakes unterscheidet sich jedoch von Moles. Wenn Sie also von Moles zu Fakes wechseln, müssen Sie einige Änderungen an Ihrem Code vornehmen. Ein Leitfaden für diese Migration wird zu einem späteren Zeitpunkt verfügbar sein.

Anforderungen: Visual Studio 11 Ultimate, .NET 4,5

1

nur seine derzeit zwar in Beta-Version verfügbar ist, denke ich, seine lohnt Haltung der shim Merkmal des neuen Fakes framework (Teil des Visual Studio 11 im Auge Beta-Version).

Shim-Typen bieten einen Mechanismus zum Umgehen einer .NET-Methode zu einem benutzerdefinierten Delegaten. Shim-Typen werden vom Fakes-Generator mit Code generiert und verwenden Delegaten, die wir Shim-Typen nennen, um die neuen Methodenimplementierungen anzugeben. Unter der Haube verwenden Shim-Typen Callbacks, die zur Laufzeit in der Methode MSIL bodies injiziert wurden.

Persönlich habe ich versucht, dies zu verwenden, um die Methoden auf versiegelten Framework-Klassen wie DrawingContext zu verspotten.

2

Ich bin kürzlich auf dieses Problem gestoßen und nach dem Lesen/Suchen im Web scheint es, als gäbe es keinen einfachen Weg um außer ein anderes Werkzeug wie oben erwähnt zu verwenden. oder rohe Dinge Handhabung wie ich es tat:

  • Instanz versiegelter Klasse erstellen, ohne genannt zu bekommen Konstruktor.
  • System.Runtime.Serialization.FormatterServices.GetUninitializedObject (instanceType);

  • Werte zuweisen Ihre Eigenschaften/Felder durch Reflexion

  • YourObject.GetType() GetProperty ("Propertyname") SetValue (dto, newValue, null)..;
  • YourObject.GetType(). GetField ("Feldname"). SetValue (dto, newValue);
+0

Dies ist die einzige Lösung, die Sinn macht und funktioniert für interne interne Microsoft-Sachen spotten, ohne zusätzliche Bibliotheken hinzuzufügen wie Fakes - danke! – Ben