Isolating WCF through Dependency Injection – Part II

02/07/2009

In my previous post, I talked about how to design a class that uses a WCF client proxy in such a way to make it testable in unit tests, without having to spin up a real service to answer the requests.
The technique I illustrated uses a particular Inversion of Control (IoC) principle called Dependency Injection (DI), in which objects that a given class depends on get pushed into it from the outside rather than being created internally.

In this particular case, the external dependency is represented by a WCF client proxy targeting a particular service. This approach enables me to swap the real proxy objects used in production with test doubles while running unit tests, making it possible to assert the class’s behavior in a completely isolated and controlled environment.

Moving from concrete classes to interfaces

Last time we left off with a MailClient class that takes an instance of ChannelFactory<MailServiceClientChannel> in the constructor and uses it internally every time it needs to download Email messages by following three steps:

  1. Creating a new proxy instance configured to communicate with the remote MailService service
  2. Invoking the GetMessages operation on the service
  3. Disposing the proxy

However, as I pointed out before, being the ChannelFactory<TChannel> a concrete class, creating a test double for it isn’t very convenient. A much better approach would be having the MailClient class interact with an interface instead. This way it would be easy to create a fake factory object in unit tests and have the class use that instead of the real one.

After a quick look in the online MSDN Documentation I found that the ChannelFactory<TChannel> class indeed implements the IChannelFactory<TChannel> interface. “Sweet, I can use that!” I thought. But there’s a catch:

info
The IChannelFactory<TChannel>.CreateChannel factory method requires the caller to pass along an EndpointAddress object, which contains information about where and how to reach the service, like the URI and the Binding.

This isn’t really what I wanted, since it forced the MailClient class to have knowledge of where the remote service is located or at the very least how to obtain that piece of information. This doesn’t really conform to the Dependency Injection principle, since these details naturally belong to the dependent object, and should therefore be handled outside the scope of the class.

The ChannelFactory adapter

The solution I came up with is to create an adapter interface to hide these details from my class. The implementation of this interface would then wrap a properly configured instance of ChannelFactory<TChannel> and delegate all the calls it receives to it.

Here is the definition of the adapter interface:

using System.ServiceModel;

public interface IClientChannelFactory<TChannel>
    where TChannel : IClientChannel 
{ 
    void Open(); 

    void Close(); 

    void Abort(); 

    TChannel CreateChannel(); 
}

And here is the default implementation:

 
using System; 
using System.ServiceModel; 

public class ClientChannelFactory<TChannel> : IClientChannelFactory<TChannel>
    where TChannel : IClientChannel 
{ 
    private ChannelFactory<TChannel> factory; 

    public ClientChannelFactory(string endpointConfigurationName) 
    { 
        this.factory = new ChannelFactory<TChannel>(endpointConfigurationName); 
    } 

    public void Open() 
    { 
        this.factory.Open(); 
    } 

    public void Close() 
    { 
        this.factory.Close(); 
    } 

    public void Abort() 
    { 
        this.factory.Abort(); 
    } 

    public TChannel CreateChannel() 
    { 
        return this.factory.CreateChannel(); 
    } 
}

As you can see by using generics we are able to create an implementation that works for different types of proxies.

Notice also that the class requires the callers to specify the name of the configuration element used to describe the service endpoint in the constructor. This way the MailClient class  is completely isolated from having to know about WCF configuration details, and can instead concentrate on its core responsibility, that is to invoke operations on the service and work with the results.

Here is the final MailClient implementation:

public class MailClient 
{ 
    private IClientChannelFactory<IMailServiceClientChannel> proxyFactory; 

    public MailClient(IClientChannelFactory<IMailServiceClientChannel> proxyFactory) 
    { 
        this.proxyFactory = proxyFactory; 
    } 

    public EmailMessage[] DownloadMessages(string smtpAddress) 
    { 
        // Validate the specified Email address 
        
        IMailServiceClientChannel proxy; 
        
        try 
        { 
            proxy = proxyFactory.CreateChannel(); 

            Mailbox request = new Mailbox(smtpAddress); 
            EmailMessage[] response = proxy.GetMessages(request) 

            // Do some processing on the results 

            proxy.Close(); 
            
            return response; 
        } 
        catch(Exception e) 
        { 
            proxy.Abort(); 

            throw new MailClientException(&quot;Failed to download Email messages&quot;, e); 
        } 
    } 
}

The only modification is in the type of the argument declared in the constructor, which is now an interface.

Finally unit testing

We are now able to test our class in isolation by creating fake objects that implement the IClientChannelFactory and IMailServiceClientChannel interfaces respectively, and inject them into our object under test.

In this particular example I am using Rhino Mocks as an isolation framework to create and manage test doubles, but I could as well used just about any other isolation framework out there, such as Typemock Isolator, with the same result.

using Microsoft.VisualStudio.TestTools.UnitTesting; 
using Rhino.Mocks; 

[TestClass] 
public class MailClientTest 
{ 
    [TestMethod] 
    public void DownloadMessages_WithValidEmailAddress_ReturnsOneMessage() 
    { 
        // Fakes out the WCF proxy
        var stubProxy = MockRepository.CreateStub<IMailServiceClientChannel>(); 

        // Stubs the service operation invoked by the class under test
        stubProxy
            .Stub(m => m.GetMessages(Arg<string>.Is.Anything))
            .Returns(new EmailMessage[0]);

        // Fakes out the WCF proxy factory
        var stubProxyFactory = MockRepository.CreateStub<IClientChannelFactory<IMailServiceClientChannel>>();

        // Stubs the factory method to return the mocked proxy
        stubProxyFactory 
            .Stub(s => s.CreateChannel()) 
            .Return(stubProxy);
 
        var testObject = new MailClient(stubProxyFactory); 

        EmailMessage[] results = testObject.DownloadMessages("test@test.com"); 

        Assert.AreEqual(0, results.Length, "The method was not supposed to return any results");
    } 
}

This concludes this short series of posts on how to apply Dependency Injection to classes that consume WCF services in order to easily test them in isolation with unit tests. I hope this helps.

/Enrico

12 Responses to “Isolating WCF through Dependency Injection – Part II”

  1. Cyr1dian Says:

    Hi Enrico,

    would you perhaps have a small downloadable sample solution? What is specifically unclear to me is (mainly from Part I) is how you configure the endpoint.

    What I am looking to do is have a client (Winform in this case) to consume a WCF service but to couple the two via Unity using a config file as much as possible. I think your articles are the closest thing I have yet found that illustrate how to do this but I’m just missing a few bits and pieces that have prevented me from getting it to work.

    Maybe even just posting your app.config would suffice.

    Thanks!


    • Hi,

      Thanks for pointing that out. I added a sample WCF configuration in the Part I post. You’ll find it towards the end of the page. Please, let me know if you would like any further clarification.

      /Enrico

  2. Jason Says:

    Enrico, can you show what you changed to set up Unity once you implemented IClientChannelFactory?


    • Hi Jason,

      I registered the IClientChannelFactory<TChannel> interface to map to the concrete ClientChannelFactory<TChannel> class in my Unity container. Here is an example:

      var container = new UnityContainer();
      container
       .RegisterType<IClientChannelFactory<IMailServiceClientChannel>>()
       .Configure<InjectedMembers>()
        .ConfigureInjectionFor<ClientChannelFactory<IMailServiceClientChannel>>(
         new InjectionConstructor("localMailServiceEndpoint"));
      

      A new instance of the ClientChannelFactory<IMailServiceClientChannel> class will now automatically be passed to every new MailClient object created through Unity.

      Hope this helps.

      /Enrico

  3. Tim Ellison Says:

    This is awesome! One question though. Our message exchange pattern is request/response and wrapped. One thing I noticed (we use Moq, not RhinoMocks or others) though is that Moq requires .Equals() implementation on the messages. Assuming other mocking libraries implement similarly, how would you deal with having to be intrusive with the svcutil-generated client?

    Tim


    • Hi Tim,

      If I understand your scenario correctly, you want to create stubs or mocks for the message objects being passed between the service and the client. In particular you need to fake the Equals method and the problem is that those classes are auto-generated so you can’t really modify them.
      In this case I would use a technique called Extract & Override, which I picked up by the excellent book The Art of Unit Testing by Roy Osherove. Using this technique you could fake an auto-generated message class by creating a new derived class and override the Equals method (which is inherited from Object) to replace its implementation with your own.

      Here is an example:

      // Auto-generated message class
      // created by the svcutil tool
      public class RequestMessage
      {
      }
      
      // Fake message class
      public class FakeRequestMessage : RequestMessage
      {
          public override Equals()
          {
              // Fake implementation
          }
      }
      

      I hope this helps.

      /Enrico

  4. David Says:

    What is IMailServiceClientChannel ? thanks

  5. threenineconsulting Says:

    This is probably one of the best samples I have found on this. However, it would be really handy to have a complete source code download, because as always with blog posts the extracts always leave out an essential bit of info.

    Please could you provide a source code download?

  6. brix Says:

    Hi,
    you go to all this effort to post a blog and then no sample provided makes all useless as sometimes you might miss something.

    Why dont you provide an example download and then you it would be perfect no?


  7. Silly question: what is the point of Open(), Close(), and Abort() in IClientChannelFactory? I see in the default implementation, these methods are just calling the internal channel factory versions. However, what would be calling these methods via IClientChannelFactory?


    • @Ralph That’s a very good observation. The reason why I chose to expose those methods in the IChannelFactoryAdapter interface is to allow the client to manage the lifetime of the internal ChannelFactory instance in a deterministic fashion. For example, one might want to reuse the same ChannelFactoryAdapter instance across different components and explicitly close it at a specific point in time, thus releasing all channels that were created by it at once.

      On the other hand, I recognize that having the Open, Close and Abort methods on both the ChannelFactory and the IClientChannel instances it creates is somewhat ambiguous. However, since we’re building an abstraction on top of the ChannelFactory (albeit a very thin one) one could take the chance to simplify its API by removing those methods from the adapter interface. This would give clients one consistent way to manage the lifetime of the channels they create.

      I hope this helps in answering your question.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 186 other followers