Oct 20, 2008

Unit Testing WCF Services

I've seen a lot of people test WCF services using integration tests.  You know, where you create a small program as a test harness that creates a client for a web service, calls the service and checks what happened.  Similarly when you want to test your client side code is using the service properly then you have to have your client talking to something, so you need to have a test service running and that can be a pain at times, especially if the service is hosted somewhere in the cloud.

Testing a WCF Service

Testing WCF services themselves is actually quite straightforward.  Since a WCF service library is really just a normal class library it means that WCF services can can actually be called and tested using NUnit, MSTest or your favourite xUnit framework without needing a proper WCF client at all as I'll show you here:

Let's start by adding a WCF Service to a service library:

image

When we do, we get two new files added to our project - MyService.cs and IMyService.cs.

MyService.cs is just a simple skeleton as shown here:

namespace WcfServiceLibrary1
{
public class MyService : IMyService
{
public void DoWork()
{
}
}
}

The interface likewise is also very simple:

namespace WcfServiceLibrary1
{
[ServiceContract]
public interface IMyService
{
[OperationContract]
void DoWork();
}
}

Nothing to it.  So let's implement something simple so that we at least have something to test :-)  How about we change DoWork() to return an int with the current hour of the day and update the interface definition to match.  Something like this:

        public int DoWork()
{
return DateTime.Now.Hour;
}

Now, being good programmers, we want to make sure our incredibly complex method works as expected so let's write a test for it.  Remembering that the MyService class is just a normal class we don't actually need to do anything via WCF at all.  Just test it like so:

        [TestMethod()]
public void DoWorkTest()
{
MyService target = new MyService();
int expected = DateTime.Now.Hour;
Assert.AreEqual(expected, target.DoWork());
}

I know the example is a little too simplistic, but you should get the idea.


Testing a WCF Client


So now that we have a working service how do call it?  And how do we test our client?  Now when we add a WCF service reference in our client project Visual Studio will generate the code for a WCF service client - it's this service client that our application will use, so is there any value in testing the client generated by Visual Studio itself?  Probably not.  So the question then becomes: how do we test that the WCF service client is being used correctly by our application code.


Let's start an example by adding a service reference to our WCF Service we created above, similar to what's shown here.  This is what we'll use in our client application.


image


Visual Studio goes ahead and adds the reference to the solution and behind the scenes creates a MyServiceClient class that we can then call from our application as shown in the following code:

        public string CallMyServiceClient()
{
int result;
MyServiceClient client = new MyServiceClient();
result = client.DoWork();
return result.ToString();
}

Code like the above is something you will see in many places around the web, however we have just made life a lot harder for ourselves than we need to.  Can you see the problem?  If I want to test the CallMyServiceClient method I have to instantiate a WCF client and call it, which also means I also have to have a WCF service running and ensure all my WCF end points are configured correctly for the test environment.  It's OK when it's a simple like this, but in a large application with many services and people involved this sort of thing can get out of hand very quickly.


Further, if I wanted some way to see what happens to my code should the service throw an exception I'd have some difficulties so my error handling code (or lack thereof) will usually go largely untested.


Thankfully there are a few simple changes we can make to make this testability problem go away.


First, have a look at the declaration of the MyServiceClient and you'll see that Visual Studio has generated something like this:

    [System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class MyServiceClient :
System.ServiceModel.ClientBase<WCFClientLibrary1.MyServiceReference.IMyService>
, WCFClientLibrary1.MyServiceReference.IMyService {

public MyServiceClient() {
}

The interesting thing to note here is that MyServiceClient is implementing the IMyService interface.  So if we wanted we could remove the concrete class reference and use the interface like so:

        public string CallMyServiceClient()
{
int result;
IMyService client = new MyServiceClient();
result = client.DoWork();
return result.ToString();
}

But this doesn't help much since we still have that instantiation of the MyServiceClient in our method to deal with.  We need to get that out of there.  To do that we'll use Dependency Injection - a technique where instead of the class creating the dependencies it needs, we pass into the class everything else that the class has a dependency on - in this case the MyServiceClient class.  Here's how we could do it:

    public class MyClass
{
private IMyService client;

public MyClass(IMyService wcfClient)
{
client = wcfClient;
}

public string CallMyServiceClient()
{
int result = client.DoWork();
return result.ToString();
}

So now when we create an instance of MyClass we pass in an instance of the MyServiceClient class so that MyClass doesn't have to create a reference to MyServiceClient.  We could do this either through our own application code higher up the call stack or through the use of an Inversion of Control container such as Unity, Castle Windsor or any of the other choices out there.


Now that we've introduced dependency injection we have also given ourselves a way to test MyClass without actually hitting the WCF service client at all.


What we need to do is to create a fake version of the MyServiceClient class that we can use as a substitute for the real WCF service client and pass that to MyClass instead.  We could, if we chose, create this fake class ourselves, but mocking frameworks exist to make our life easier in the regard and come with a bunch of extra features that let us do things such as setting return values when calls are made, without us having to write that ourselves.


Here's a test we might use with our client using the Rhino Mocks framework:

        [TestMethod()]
public void ClientTest()
{
IMyService mock = MockRepository.GenerateMock<IMyService>();
mock.Expect(t => t.DoWork()).Return(10);

MyClass classUnderTest = new MyClass(mock);

Assert.AreEqual("10", classUnderTest.CallMyServiceClient());
mock.VerifyAllExpectations();
}

Let's have a look at this test in a little more detail.  What are we doing here?



  1. We're creating a fake version of the IMyService class called "mock".
  2. We then tell the mock object to return the value 10 when its DoWork method is called.
  3. Next we create a MyClass instance and pass the fake class through to it in place of a real WCF service client.
  4. Then we call our classUnderTest and assert that the method returned the expected value
  5. And on the last line we also check that the DoWork() method was called on our mock object (i.e. we didn't just return from MyClass without calling the IMyService method)

This is quite useful as we now have a way to test our application code is making the appropriate calls to the WCF client without needing to actually spin up a full WCF client & service nor do we have to do any testing across the wire.  This example is also simple enough to extend so that when the DoWork() method is called we can throw a WCF exception instead to test how well our class handles failures.


I hope this helps you improve your testing with WCF in the future.  Good luck!

8 comments:

  1. Great post Richard, nice and succinct and answers a question many have had.

    One thing I would point out is that WCF exceptions can be quite bothersome in this scenario. When an exception is raised while calling the service, you'll get told that the IMyService can no longer be used because the channel has been torn down.

    One solution would be to have a new IMyService created every time from the IOC container. Instead of taking an instance of IMyService in the constructor, you might take a factory or the IOC container, and each time you'd resolve a new instance. You'd really want to make sure the service is not registered as a Singleton (otherwise you just recreated MS Outlook :)).

    However, creating proxies can be expensive - for HTTP services maybe it isn't so bad, for NetTcp it can be a lot worse. So creating a new proxy each time isn't a great idea.

    Another technique would be to catch the exception, and null/re-create the proxy from the factory/IOC container. But then that means your class has some expectations about how your services behave. If you swapped it for another web service architecture, re-using proxies that have thrown exceptions might be OK; it's too much knowledge for the consumer.

    So my approach would be to build a Castle Windsor interceptor or EntLib Policy Injection handler to intercept the service calls, catch the exception, and re-create an internal proxy. It would look like this:

    Client -> IMyService (auto-generated interceptor) -> MyServiceClient (wrapped proxy) -> web service

    But that's a lot of work :)

    ReplyDelete
  2. Also consider that your IMyService won't usually have a Dispose method, while your proxy client usually will, and you really ought to call it when you are done.

    interface IMyServiceClient // mock this
    : IMyService, IDisposable
    {

    }

    One alternative is that using a lifecycle aware container like Castle means you can call Container.Release(myService) and it'll take care of calling Dispose for you, so your client code doesn't need to know if it's IDisposable or not.

    ReplyDelete
  3. one problem i've found is in my code i'll be calling the async versions of methods.
    However the interfaces don't define these, just the normal synchronous methods.
    This means i can't write my standard code against an interface instead of the concrete WCF method

    ReplyDelete
  4. When does the proxy get closed in CallMyServiceClient?

    ReplyDelete
  5. Actually the above comment re. closing the proxy is a valid one - in switching from the concrete class to using the service interface, I too noticed that I could no longer call Close() - is this handled behins the scenes..?

    ReplyDelete
  6. I have a question about this test - If I am reading this right the ClientTest test method

    1. Creates a mock of the service
    2. Mocks the return value
    3. I am going to paraphrase this one: Call my mock.DoWork function through an instance of MyClass.

    4. Asserts that the mocked return is equal to 10 which you set it to when seting up the mock.

    My Question: This test method never executes any code that would be in the DoWork function which was the original goal. Is this code demonstrating anything more than the fact that the mocked service is return 10 as specified by .Return(10);

    ReplyDelete
  7. @Anonymous On the client side you don't want to test the service, you want to test what you do with the result. It's on the server side that you want to test that the service produces the results you expect.

    So yes, you're right about the ClientTest method not execute the real DoWork method. In a unit test we don't need to (nor do we want to). n fact we can trust that the real DoWork method is correct because we have the server side tests for it already. The confusion may come from the sample client side code being quite simple. In real world code we would be do something with the result other than calling ToString() on it :-)

    ReplyDelete
  8. Congratulations on your post!

    Here's another way that doesn't involve coding the unit tests at all. I think is more easier and faster:

    http://jordi-montana.blogspot.com.es/2012/03/testing-wcf-restful-services-with-post.html

    ReplyDelete