10 Stimmen

Einfacher Weg, einen WCF-Dienst nachzubilden?

Ich habe eine Anwendung, die einen WCF-Dienst verwendet. Jetzt möchte ich Unit-Tests zu der App hinzufügen.

In einigen Fällen muss ich den WCF-Dienst nachahmen, da es manchmal schwierig ist, das gewünschte Verhalten vom Dienst zu erhalten (z. B. wenn der Dienst spezielle Ausnahmen auslöst).

Ich könnte dem wcf-Client noch eine weitere Schnittstelle hinzufügen, aber das erscheint mir ein wenig albern, da die Client-Aufrufe bereits eine Schnittstelle verwenden.

Gibt es eine einfache Möglichkeit, einen WCF-Dienst nachzubilden? Einfacher als eine weitere Schnittstellenschicht zu erstellen und jeden einzelnen WCF-Aufruf innerhalb dieser Schicht umzuleiten?

Bearbeiten: Die meisten Antworten scheinen nicht viel über die Verwendung von WCF-Diensten zu wissen, daher einige Klarstellungen:
Um einen WCF-Dienst von einem ViewModel aus zu nutzen, muss ich die Verbindung etwa so verwalten:

ChannelFactory<IMyWcfService> channelFactory = new ChannelFactory<IMyWcfService>("");
IMyWcfService proxy = channelFactory.CreateChannel();
proxy.CallMyStuff();
proxy.Close();

Ich kann dem ViewModel nicht einfach den Proxy an die WCF übergeben, da die Verbindung für jede Transaktion geöffnet und geschlossen werden muss. Aus diesem Grund würde die Verwendung von RhinoMock/NMock nicht funktionieren, da sie ein ViewModel benötigen, das den Proxy als Parameter erhält, was bei der Verwendung von WCF nicht so möglich ist.

9voto

David Schmitt Punkte 56455

Warum können Sie nicht etwas verwenden wie NMock2 um sich über die IMyWcfService Schnittstellen direkt?

Wenn Sie in der Lage sein müssen, neue Instanzen im laufenden Betrieb zu erstellen, verwenden Sie die Factory, um die ChannelFactory<IMyWcfService> vom Kunden. Auf diese Weise können Sie die Fabrik ersetzen, indem Sie dem Client eine Fabrik zur Verfügung stellen, die Mocks anstelle von echten Proxys erstellt.

4voto

Alex Punkte 3950

Sie können Sie Moq Spotting-Framework. Basierend auf dem Beispiel, das Sie zur Verfügung gestellt haben:

ChannelFactory<IMyWcfService> channelFactory = new ChannelFactory<IMyWcfService>("");
IMyWcfService proxy = channelFactory.CreateChannel();
proxy.CallMyStuff();
proxy.Close();

So sieht eine Mocking-Implementierung aus:

Mock<IMyWcfServiceChannel> channelMock = new Mock<IMyWcfServiceChannel>(MockBehavior.Strict);
channelMock
    .Setup(c => c.CallMyStuff())
    .Returns("");

string myStuff = channelMock.Object.CallMyStuff();

Nachdem Sie einen Proxy für die WCF Dienst - Sie sollten eine channel Schnittstelle, die Ihnen zur Verfügung steht, genannt IMyWcfServiceChannel .

Je nach Rückgabetyp der von Ihnen aufgerufenen Dienstmethode können Sie so gut wie jede Ausgabe festlegen. In dem obigen Beispiel habe ich string Typ als Beispiel.

Um die obige Lösung effizienter nutzen zu können, sollten Sie 2 Konstruktoren für die Geschäftsschicht erstellen:

public class Example1
{
    IMyWcfServiceChannel _client;

    public Example1()
    {
        var factory = new ChannelFactory<IMyWcfServiceChannel>("binding");
        _client = factory.CreateChannel();
    }

    public Example1(IMyWcfServiceChannel client)
    {
        _client = client;
    }

    public string CallMyStuff()
    {
        return _client.CallMyStuff();
    }
}

Also auf der prod Sie verwenden einen parameterlosen Konstruktor. Unter unit Tests verwenden Sie einen parameterreichen Konstruktor und übergeben ihm einen Mock ( channelMock.Object ).

2voto

Michael Mann Punkte 757

Sie können ein beliebiges Mocking-Framework wie RhinoMocks oder NMock verwenden, um den Schnittstellenkontrakt zu spiegeln. Wenn also Ihr Dienst IMyService implementiert hat, können Sie ein Mocking-Framework verwenden, um Erwartungen an die Methodenaufrufe auf dieser Schnittstelle zu setzen. Wenn Sie mit diesem Konzept nicht vertraut sind, können Sie einfach ein Stand-in-Objekt erstellen, das IMyService implementiert, aber während Ihrer Tests vorgibt, der echte Dienst zu sein. Auf diese Weise werden die Methoden, wenn sie aufgerufen werden, in Ihrem Stand-in-Objekt aufgerufen, und Sie können Ihr Stand-in-Objekt zurückgeben lassen, was immer Sie wollen.

1voto

codeMonkey Punkte 3188

Ich bin mit FakeItEasy, die nicht für Mocking von OperationContext erlaubt, weil es eine versiegelte Klasse ist. Fügen Sie Abhängigkeit Injektion in den Mix, und Sie haben sich eine straight-up-Albtraum. Ich habe eine Woche damit verbracht, dieses Zeug herauszufinden, und hier ist, was ich schließlich herausgefunden habe Sie werden ein paar von ServiceHost und dann werden sie miteinander kommunizieren und Ihren gesamten Code dazwischen ausführen.

1 Erstellen Sie eine Klasse, die erbt von ClientBase<IMyWcfService> :

public class MyWcfServiceClient : ClientBase<IMyWcfService>, IMyWcfService
{
    public MyWcfServiceClient(string address)
        : base(new WebHttpBinding(), new EndpointAddress(address))
    {
        this.Endpoint.EndpointBehaviors.Add(new WebHttpBehavior());
    }

    public void CallMyStuff()
    {
        using (new OperationContextScope(this.InnerChannel))
        {
            base.Channel.CallMyStuff();
        }
    }
}

2 Erstellen Sie einen "aufrufenden Dienst", der diese Methode aufrufen wird. Erstellen Sie zunächst eine Schnittstelle:

[ServiceContract]
public interface IMyWcfCallingService
{
    [OperationContract]
    void CallCallMyStuff();
}

Erstellen Sie dann den "aufrufenden Dienst", der diese Schnittstelle implementiert:

public class MyWcfCallingService : IMyWcfCallingService
{
    static MyWcfServiceClient _client = new MyWcfServiceClient("http://localhost:8008");
    // ^^^ This "http://localhost:8008" is the address where
    // your actual service is going to "live" in your unit test        

    public void CallCallMyStuff()
    {
        _client.CallMyStuff();
    }
}

3 Instanziieren Sie Ihren eigentlichen Dienst im Einheitstest:

var myService = new MyWcfService(_someMockedDependency, _someOtherMockedDependency);

4 Erstellen Sie die beiden ServiceHost die miteinander sprechen und Ihren Dienst anrufen werden:

var restHost = new WebServiceHost(myService, new Uri("http://localhost:8008"));
// ^^^ Make sure the URL here matches the URL you used in Step #2

var behavior = restHost.Description.Behaviors.Find<ServiceBehaviorAttribute>();
behavior.InstanceContextMode = InstanceContextMode.Single;
// ^^^ If you're using dependency injection with mocked dependencies 
// to create your myService object, this is muy importante

restHost.Open();

var clientHost = new ServiceHost(typeof(MyWcfCallingService), new Uri("http://localhost:80"));
// ^^^ use some other address here, different from the one you used for the service itself
clientHost.AddServiceEndpoint(typeof(IMyWcfCallingService), new BasicHttpBinding(), string.Empty);
clientHost.Open();

var factory = new ChannelFactory<IMyWcfCallingService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:80"));
// ^^^ this should match the url for the clienthost
var proxy = factory.CreateChannel();
proxy.CallCallMyStuff();

Verdammt sei die WCF! Ich habe WebApi nie so sehr geschätzt, wie als ich anfing, mich durch veralteten WCF-Code zu wühlen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X