10

I've got an app which is using a WCF service. Now I'd like to add unit tests to the app.

For some cases I need to mock the WCF service, since getting the desired behaviour from the service sometimes is tough (e.g. service throws special exceptions).

I could add yet another interface to the wcf client, but this seems a bit silly, since the client calls already are using an interface.

Is there an easy way to mock a WCF service? Easier than creating another interface layer and redirecting every single WCF call inside it?

Edit: Most of the answers seem not to know much about WCF service using, so some clarification:
To use a WCF service from a ViewModel, I have to manage the connection something like this:

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

I can't just pass the ViewModel the proxy to the WCF, since the connection needs to be opened and closed for every transaction. For this reason using RhinoMock/NMock would not work, since they need a ViewModel which gets the proxy as a parameter, which can't be done like this if you use WCF.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Sam
  • 28,421
  • 49
  • 167
  • 247

4 Answers4

9

Why can't you use something like NMock2 to mock the IMyWcfService interfaces directly?

If you need to be able to create new instances on the fly, use the Factory to hide the ChannelFactory<IMyWcfService> from the client. This way you can replace the factory, providing the client one which creates mocks instead of real proxies.

David Schmitt
  • 58,259
  • 26
  • 121
  • 165
3

You can you Moq mocking framework. Based on the example that you have provided:

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

Here is how a mocking implementation will look like:

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

string myStuff = channelMock.Object.CallMyStuff();

After you have added a proxy for the WCF service - you should have a channel interface available to you, called IMyWcfServiceChannel.

Depending on the return type of service method you are calling - you can set just about any output. In the example above I used string type as an example.

In order to use the above solution more efficiently you might want to create 2 constructors for the business layer like so:

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();
    }
}

So on the prod you use parameter-less constructor. In unit tests you use parameter-full constructor and pass mock to it (channelMock.Object).

Alex
  • 4,607
  • 9
  • 61
  • 99
2

You can use any mocking framework like RhinoMocks or NMock, to mock out the interface contract, so if your service implemented IMyService then you could use a mocking framework to set expectations on the method calls on that interface. If you are not familiar with this concept then you can simply create a stand-in object that implements IMyService but pretends to be the real service during your testing. This way when the methods are called they are called on your stand-in object and you can have your stand-in return whatever you want it to.

Michael Mann
  • 777
  • 3
  • 9
  • Using RhinoMock/NMock I would need to open the connection to the WCF service, pass the proxy to the ViewModel, and close it after use. Since WCF connections usually have a short timeout, and should be opened and closed for every transaction, this is a no-go. – Sam Jan 09 '09 at 13:50
  • In other words: you can't use RhinoMock/NMock to mock an WCF interface. At least not that I know of, if you know a way, please share it. – Sam Jan 09 '09 at 14:14
1

I am using FakeItEasy, which doesn't allow for mocking of OperationContext because it's a sealed class. Add dependency injection into the mix, and you've got yourself a straight-up nightmare. I've spent a week trying to figure this stuff out, and here's what I finally came up with . . . you're going to create a couple of ServiceHost's, and then they're going to talk to each other and run all your code in between.

#1 Create a class that inherits from 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 Create a "calling service" that will call this method. First, create an interface:

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

Then create the "calling service" that implements this interface:

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 Instantiate your actual service in the unit test:

var myService = new MyWcfService(_someMockedDependency, _someOtherMockedDependency);

#4 Create the two ServiceHost's that are going to talk to one another, and call your service:

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();

Damn WCF all to hell! I never appreciated WebApi so much as when I started having to dig through legacy WCF code.

codeMonkey
  • 4,134
  • 2
  • 31
  • 50