Monday 15 October 2012

Mocking WCF client with Moq

Performing basic web service calls from your code using WCF is relativelly easy. All you have to do is add a new service reference to your project, pointing to the service url. WCF will automatically generate a client class for you, that you can use to call service methods.

The web service

Let's say we have a web service that performs various string transformation e.g. reverses specified strings (obviously it's not a real life scenario, as you wouldn't normally call a web serviec to do that). The service implements the following interface:

[ServiceContract]
public interface IStringService
{
    [OperationContract]
    string ReverseString(string input);
}
In the sample code for this post I created a basic WCF implementation of that service. However, the service itself doesn't need to be created using WCF, as long as it's using SOAP.

The client

The simplest class that consumes this service could look as follows:
public class StringHelper
{
    StringServiceClient _client;

    public StringHelper()
    {
        _client = = new StringServiceClient();
    }

    public string Reverse(string input)
    {
        return _client.ReverseString(input);
    }
}
The StringServiceClient is a class generated automatically when adding a service reference. All you have to do is istantiate it and call the chosen method.

There is one issue with that approach though: you cannot unit test your StringHelper.Reverse method, without actually calling the web service (because classes are strongly coupled). When writing proper unit tests you should mock all the class dependencies, so you can only focus on a single unit of code. Otherwise it becomes an integration test.

When using Moq you can only mock interfaces or virtual methods. The generated StringServiceClient doesn't implement any interface that would expose the service contract. Also, methods generated in that class are not virtual.

Luckly enough the code generated when adding the service reference contains the Channel interface that we can use. The channel interface extends the service contract interface, so you can invoke all service methods using its implementation. This mean we can update the client app can to remove the tight coupling:

public class StringHelper
{
    IStringServiceChannel _client;

    public StringHelper()
    {
        var factory = new ChannelFactory<IStringServiceChannel>("BasicHttpBinding_IStringService");
        _client = factory.CreateChannel(); 
    }

    public StringHelper(IStringServiceChannel client)
    {
        _client = client;
    }

    public string ReverseString(string input)
    {
        return _client.ReverseString(input);
    }
}
As you can see, instead of working with the generated client we create a channel instance using ChannelFactory and the binding name "BasicHttpBinding_IStringService". The binding name can be found in the app.config file. The app.config file is automatically updated with WCF enpoint configuration when adding the service reference.

Testing

A simple integration test for our client code:
[TestMethod]
public void TestStringHelper_Reverse()
{
    StringHelper sh = new StringHelper();
    string result = sh.Reverse("abc");
    Assert.AreEqual("cba", result);
}
This test would work with both versions of the client presented above.

Now for the actuall unit tests that mocks the service channel object using Moq:

[TestMethod]
public void TestStringHelper_Reverse()
{
    // Create channel mock
    Mock<IStringServiceChannel> channelMock = new Mock<IStringServiceChannel>(MockBehavior.Strict);

    // setup the mock to expect the Reverse method to be called
    channelMock.Setup(c => c.ReverseString("abc")).Returns("cba");

    // create string helper and invoke the Reverse method
    StringHelper sh = new StringHelper(channelMock.Object);
    string result = sh.Reverse("abc");
    Assert.AreEqual("cba", result);

    //verify that the method was called on the mock
    channelMock.Verify(c => c.ReverseString("abc"), Times.Once());
}

Sample code for the service, client & test on github:
https://github.com/filipczaja/BlogSamples/tree/master/MockingWCFClientWithMoq

3 comments:

Unknown said...

Cool post, easy to understand and well structured.
Thank you!

Unknown said...

Just curious as to what the reasoning is behind mocking Mock vs Mock? I Get that your Mock exposes more functionality probably closer to how it could be used (IDispoable used in a using block for example) but that's not being done in your case. Any input?

Unknown said...

Heh... my last comment didn't come out to well from the brackets.

I'll try again:

Just curious as to what the reasoning is behind mocking Mocking IStringServiceChannel vs Mocking IStringService? I Get that your Mocking IStringServiceChannel exposes more functionality - probably closer to how it could actually be used (IDispoable used in a using block for example) but that's not being done in your case. Any input?