So you're familiar with TDD (test-driven development
)... Failing your tests first, then building up your classes to satisfy your tests so they're all "Green
This is a typical scenario and is pretty straightfoward with .NET.
Well now you have to create a WCF service and implement unit testing to ensure you don't break your service when you build out a new version.
This is a bit more challenging but you do have some different options:
- Separate out your service code into a library and run your tests against the library code...
- This really only tests your library code and doesn't actually allow you to instate the service itself to perform a realistic test. For example, you might inadvertently forget to decorate a field with a [DataMember] attribute. This wouldn't affect your unit test if you're just testing your library code but could break your service if a client is expecting that field to be serialized.
- Use an IoC (Inversion of Control) container to inject your dependencies from within your tests
- This is the most accurate way to effectively unit test your service. Granted, there is a bit more setup involved up front, however this approach is defintely worth the effort once you wrap your head around the concept of WCF extension points
We are going to be focusing on option #2 since we want our tests to be as realistic and thorough as possible.
To help us get started, let's ask ourselves the following questions:
- Why do we inject a dependency into our service?
- What type of dependency do we inject into our service?
- How do we actually inject a dependency into our service?
NOTE: For our examples we will be utilizing Microsoft's Unity application block, however you can just as easily use any IoC container that you are comfortable with such as StructureMap, etc. The actual IoC code is found within our WCF extension points, which you will see described later on.
Why do we inject a dependency into our service?
Dependency injection allows one object's configuration (fields, properties, etc) to be set and/or modified by another external object.
What type of dependency do we inject into our service?
For our purpose, we will be sending in a depency to allow the service to read from a "mock" datastore (XML file) to return a collection of users rather than from the service's default SQL datastore. This way, we will know exactly what the data will look like going into and coming out of the service.
How do we actually inject a dependency into our service?
We will employ dependency injection by passing a data provider interface to the service contructor. How does the service know what concrete implementation of the data provider to use? That's where the IoC container, Unity in our case, comes into play.
Here is a checklist of the tasks we need to employ to allow the service to accept the DI (dependency injection).
- Create an interface for our data provider
- Modify our service to accept the interface in the contructor
- Create our WCF extension points to allow our IoC container to instatiate a concrete implementation of our data provider
- Create our own instance provider that inherits from IInstanceProvider
- Create a custom service behavior that inherits from IServiceBehavior which will allow us to configure the service to use our new instance provider rather than the default WCF instance provider
- We then extend the ServiceHost class and override the OnOpening() method to add our new service behavior to the ServiceDescription Behaviors collection
- Finally we need to extend the ServiceHostFactory class and override the CreateServiceHost method to use our new service host
- Now that our extension points are all setup, we need to configure the service to use the new service host factory
1.) Create an interface for our data provider
This interface contains one method, AllUserData(), that returns a collection of Users.
1: public interface IRepository
3: List<User> AllUserData();
2. Modify our service to accept the interface in the contructor
Now we have to modify our service so that we can pass in a rerence to IRepository in the constructor. Now when the GetUsers() method is called, the concrete implementation of IRepository will return it's version of the AllUserData() method.
1: public class UserService : IUserService
3: private readonly IRepository userrepository;
5: public UserService(IRepository userrepository)
7: this.userrepository = userrepository;
10: public List<User> GetUsers()
12: return userrepository.AllUserData();
Are we done? Not quite...
Trying to run this "as-is" will generate an error since WCF can only instantiate our service using a constructor with no arguments. That is why we need to create our own extension points so that we can control the instatiation of the service without having to create a no args constructor. The beauty in this is that when a client uses the service, they only see the default "no args" constructor, however when we instantiate the service through our unit tests, we will be able to see the second constructor so that we can pass in our own implementation of IRepository to "fake out" the service.
3a. Create our instance provider
This is the class where we are setting up our IoC container to resolve the IRepository interface to the correct concrete implemenation, which you'll see described later when we create the service host factory class. Notice line 19 below, this is where Unity is resolving the passed in serviceType.
1: public class WcfSvcInstanceProvider : IInstanceProvider
3: private readonly IUnityContainer container;
4: private readonly Type _serviceType;
6: public WcfSvcInstanceProvider(IUnityContainer container, Type serviceType)
8: this.container = container;
9: _serviceType = serviceType;
12: public object GetInstance(InstanceContext instanceContext)
14: return GetInstance(instanceContext, null);
17: public object GetInstance(InstanceContext instanceContext, Message message)
19: return container.Resolve(_serviceType);
22: public void ReleaseInstance(InstanceContext instanceContext, object instance)
3b. Create our service behavior
Once we have setup our instance provider, we need to incorporate that into a custom service behavior. Starting at line 12 you can see how we search for all endpoints in all channels and set their respective instance providers to our newly created provider.
1: public class WcfSvcServiceBehavior : IServiceBehavior
3: private readonly IUnityContainer container;
5: public WcfSvcServiceBehavior(IUnityContainer container)
7: this.container = container;
10: public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
12: foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
14: ChannelDispatcher cd = cdb as ChannelDispatcher;
15: if (cd != null)
17: foreach (EndpointDispatcher ed in cd.Endpoints)
19: ed.DispatchRuntime.InstanceProvider = new WcfSvcInstanceProvider(this.container, serviceDescription.ServiceType);
25: public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
29: public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
3c. Create our service host
Here we are creating our own service host and overriding the OnOpening() method to add our custom service behavior to the ServiceDescription behaviors collection.
1: public class WcfSvcServiceHost : ServiceHost
3: private IUnityContainer unityContainer;
5: public WcfSvcServiceHost(IUnityContainer unityContainer, Type serviceType, params Uri baseAddresses)
6: : base(serviceType, baseAddresses)
8: this.unityContainer = unityContainer;
11: protected override void OnOpening()
15: if (this.Description.Behaviors.Find<WcfSvcServiceBehavior>() == null)
17: this.Description.Behaviors.Add(new WcfSvcServiceBehavior(this.unityContainer));
3d. Create our service host factory
Now we create our service host factory to instantiate our service host. This is also where we are defining our concrete implementation of the IRepository interface to be of type SqlRepository. This concrete type will always be used when the service is instantiated with the no-args constructor.
1: public class WcfSvcServiceHostFactory : ServiceHostFactory
3: protected override ServiceHost CreateServiceHost(Type serviceType, Uri baseAddresses)
5: UnityContainer unityContainer = new UnityContainer();
7: unityContainer.RegisterType<IRepository, SqlRepository>();
9: return new WcfSvcServiceHost(unityContainer, serviceType, baseAddresses);
4. Configure the service to use our service host factory
Since we are hosting the service in IIS, we simply need to add one line of code to the .svc file to use our service host factory
<%@ ServiceHost Language="C#"
You should now be able to successfully run your "unit test ready" WCF service.
Now you can just include your service in your unit test project and create your tests like this:
2: public class UserServiceTests
4: UnityContainer unityContainer = new UnityContainer();
6: public UserServiceTests()
8: unityContainer.RegisterType<IRepository, TestRepository>();
12: public void GetMockedUsers()
14: UserService usr_svc = new UserService(unityContainer.Resolve<IRepository>());
16: List<User> testuserdata = usr_svc.GetUsers();
18: Assert.IsTrue(MockDatafromXmlFile(), testuserdata));
First we are telling Unity to register the TestRepository class as the concrete implementation of the IRepository interface in the test class constructor. TestRepository is simply our class that returns data from an XML file that contains a list of users.
Then in our test we simply pass in the the IRepository interface to the service constructor and test away!
Related post: Integration testing your WCF service with Unity