This documentation is archived and is not being maintained.

Unit Testing: Introduction

Visual Studio 2010

Applies to: Windows Communication Foundation

Published: June 2011

Author: Alex Culp

This topic contains the following sections.

Unit tests are effective because you, the developer, know the implementation details about the unit of work (typically a method) that you want to test. A good unit test should not cross class boundaries, and it certainly should not cross network boundaries to access external resources such as a database. This restriction requires that you give careful thought to the design of your service, to ensure it is testable.

To unit test business logic in isolation from dependencies such as databases, files, or even other services, you must abstract those dependencies so that they can be replaced with a test implementation. Create interfaces for all dependencies, and consume those dependencies only through those interfaces. Interfaces and dependency injection are two of the key aspects of the SOLID principles. (The "D" in SOLID stands for Dependency Inversion Principle).

The Unity Application Block (Unity) is available either as part of Microsoft Enterprise Library, or by itself. It is one of many effective .NET Framework tools for using dependency injection. Unity can do type resolution based on configuration, or register dependencies directly from the code. To use Unity, you can either download Enterprise Library, or you can download just the Unity Application Block.

Wrapping Unity

It is a good idea to create at least a lightweight wrapper around Unity to abstract the creation of the container, and to make it possible to use a different dependency injection provider, if you want. The samples in this article reference the wrapper that is shown in the following code. It is a very lightweight wrapper that creates only the container for you. You can, of course modify the code to suit your needs.

/// <summary>
/// Simple wrapper for unity resolution.
/// </summary>
public class DependencyFactory
{
    private static IUnityContainer _container;

    /// <summary>
    /// Public reference to the unity container which will 
    /// allow the ability to register instrances or take 
    /// other actions on the container.
    /// </summary>
    public static IUnityContainer Container
    {
        get
        {
            return _container;
        }
        private set
        {
            _container = value;
        }
    }

    /// <summary>
    /// Static constructor for DependencyFactory which will 
    /// initialize the unity container.
    /// </summary>
    static DependencyFactory()
    {
        var container = new UnityContainer();
            
        var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
        if (section != null)
        {
            section.Configure(container);
        }
        _container = container;
        }

    /// <summary>
    /// Resolves the type parameter T to an instance of the appropriate type.
    /// </summary>
    /// <typeparam name="T">Type of object to return</typeparam>
    public static T Resolve<T>()
    {
        T ret = default(T);
 
        if (Container.IsRegistered(typeof(T)))
        {
            ret = Container.Resolve<T>();
        }
 
        return ret;
    }
}

Using Configuration Files

Developers can configure dependency injection for their services using configuration files or they can register dependencies through code. To avoid having a really large web.config file, it is a good practice to put your unity configuration in a separate configuration file. The unity configuration file maps the service's interfaces to their concrete implementations. This approach is very flexible, and allows you to plug in new implementations without having to recompile the code. It is also a good practice to create a wrapper class to both abstract the complexities of Unity, and also create an option to change dependency injection providers in the future. The design of the Unity wrapper class should include the ability to load dependencies either by using a configuration file, or by creating an empty container that allows the code to register dependencies. The following example shows how to provide both of these options.

Example Unity Wrapper Code Snippet

var container = new UnityContainer();       
var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
if (section != null)
{
    section.Configure(container);
}
_container = container;

When this code executes, the container is available even if there is no Unity configuration section in the configuration file. The container allows you to register additional dependencies, or to use a custom ServiceHostFactory.

Creating a Custom ServiceHostFactory

Using configuration files to manage dependencies can quickly become impractical. As the number of services and dependencies increase, the complexity in managing multiple files overrides the benefit of flexibility. The alternative to configuration files is to register the dependencies from code. This approach is less flexible, because it requires that you recompile the code when the dependencies change. However, in practice, this is usually not a big drawback. Changes to configuration also require that you deploy these changes to production servers. In addition, in most companies, any change to the production servers requires the same level of testing and paperwork, whether the change is in configuration or in code. As a result, in an enterprise environment, the flexibility you get from configuration files is largely overrated. Instead, a custom ServiceHostFactory enables you to reduce the number of constructors to just one, the "greedy" constructor, which is the constructor with all of the parameters.

NoteNote

You cannot use a custom ServiceHostFactory if the service's InstanceContextMode property is set to Single.

To write a custom ServiceHostFactory requires six steps. They are:

  1. Create the instance provider.

  2. Create the service behavior.

  3. Add the service behavior to the service.

  4. Create a custom ServiceHostFactory.

  5. Create a concrete implementation of the DependencyInjectionServiceHostFactory class.

  6. Configure the SVC file.

Step 1: Create the Instance Provider

First create a custom instance provider that uses Unity to resolve the instance to the service, rather than allowing WCF to create the instance of the service itself. To create a custom instance provider, create a class that implements IInstanceProvider. This is shown in the following code.

/// <summary>
/// A custom instance provider that uses the ServiceLocator from MS Pattern and Practices to resolve service types.
/// </summary>
/// <remarks>
public class DependencyInjectionInstanceProvider : IInstanceProvider
{
    private readonly Type _serviceType;

    /// <summary>
    /// Initializes a new instance of the <see cref="DependencyInjectionInstanceProvider"/> class.
    /// </summary>
    /// <param name="serviceType">Type of the service.</param>
    public DependencyInjectionInstanceProvider(Type serviceType)
    {
        _serviceType = serviceType;
    }

    /// <summary>
    /// Returns a service object given the specified <see cref="T:System.ServiceModel.InstanceContext"/> object.
    /// </summary>
    /// <param name="instanceContext">The current <see cref="T:System.ServiceModel.InstanceContext"/> object.</param>
    /// <returns>A user-defined service object.</returns>
    public object GetInstance(InstanceContext instanceContext)
    {
        return GetInstance(instanceContext, null);
    }

    /// <summary>
    /// Returns a service object given the specified <see cref="T:System.ServiceModel.InstanceContext"/> object.
    /// </summary>
    /// <param name="instanceContext">The current <see cref="T:System.ServiceModel.InstanceContext"/> object.</param>
    /// <param name="message">The message that triggered the creation of a service object.</param>
    /// <returns>The service object.</returns>
    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return DependencyFactory.Resolve(_serviceType);  
    }

    /// <summary>
    /// Called when an <see cref="T:System.ServiceModel.InstanceContext"/> object recycles a service object.
    /// </summary>
    /// <param name="instanceContext">The service's instance context.</param>
    /// <param name="instance">The service object to be recycled.</param>
    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
    }
}

Step 2: Create the Service Behavior

The next step is to create a service behavior that registers the instance provider. This is shown in the following code.

/// <summary>
/// A custom service behavior which will register the dependency injection instance provider.
/// </summary>
public class DependencyInjectionServiceBehavior : IServiceBehavior
{
    #region IServiceBehavior Members

    /// <summary>
    /// Provides the ability to pass custom data to binding elements to support the contract implementation.
    /// </summary>
    /// <param name="serviceDescription">The service description of the service.</param>
    /// <param name="serviceHostBase">The host of the service.</param>
    /// <param name="endpoints">The service endpoints.</param>
    /// <param name="bindingParameters">Custom objects to which binding elements have access.</param>
    public void AddBindingParameters(ServiceDescription serviceDescription, 
        System.ServiceModel.ServiceHostBase serviceHostBase, 
        System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, 
        System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    { }

    /// <summary>
    /// Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
    /// </summary>
    /// <param name="serviceDescription">The service description.</param>
    /// <param name="serviceHostBase">The host that is currently being built.</param>
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
    {
        foreach (var cdb in serviceHostBase.ChannelDispatchers)
        {
            var cd = cdb as ChannelDispatcher;

            if (cd != null)
            {
                foreach (var ed in cd.Endpoints)
                {
                    ed.DispatchRuntime.InstanceProvider = new DependencyInjectionInstanceProvider(serviceDescription.ServiceType);
                }
            }
        }
    }

    /// <summary>
    /// Provides the ability to inspect the service host and the service description to confirm that the service can run successfully.
    /// </summary>
    /// <param name="serviceDescription">The service description.</param>
    /// <param name="serviceHostBase">The service host that is currently being constructed.</param>
    public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
    { }

    #endregion
}

Step 3: Add the Service Behavior to the Service

To register the service behavior with the service, create a custom ServiceHost. This is shown in the following code.

/// <summary>
/// This service host is used to set up the service behavior that replaces the instance provider to use dependency injection.
/// </summary>
public class DependencyInjectionServiceHost : ServiceHost
{
    /// <summary>
    /// Initializes a new instance of the <see cref="DependencyInjectionServiceHost"/> class.
    /// </summary>
    /// <param name="serviceType">Type of the service.</param>
    /// <param name="baseAddresses">The base addresses.</param>
    public DependencyInjectionServiceHost(Type serviceType, Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    { }

    /// <summary>
    /// Opens the channel dispatchers.
    /// </summary>
    /// <param name="timeout">The <see cref="T:System.Timespan"/> that specifies how long the on-open operation has to complete before timing out.</param>
    protected override void OnOpen(TimeSpan timeout)
    {
        Description.Behaviors.Add(new DependencyInjectionServiceBehavior());
        base.OnOpen(timeout);
    }
}

Step 4: Create a Custom ServiceHostFactory

The next step creates the custom ServiceHostFactory class that creates a new instance of the DependencyInjectionServiceHost class, and that provides an abstract method for registering any dependencies. This is shown in the following code.

/// <summary>
/// Produces <see cref="DependencyInjectionServiceHost"/>s.
/// </summary>
public abstract class DependencyInjectionServiceHostFactory : ServiceHostFactory
{
    /// <summary>
    /// Creates a <see cref="DependencyInjectionServiceHost"/> for a specified type of service with a specific base address. 
    /// </summary>
    /// <returns>
    /// A <see cref="DependencyInjectionServiceHost"/> for the type of service specified with a specific base address.
    /// </returns>
    /// <param name="serviceType">
    /// Specifies the type of service to host. 
    /// </param>
    /// <param name="baseAddresses">
    /// The <see cref="T:System.Array"/> of type <see cref="T:System.Uri"/> that contains the base addresses for the service hosted.
    /// </param>
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        //Register the service as a type so it can be found from the instance provider
        DependencyFactory.Container.RegisterType(serviceType);

        RegisterDependencies();
        return new DependencyInjectionServiceHost(serviceType, baseAddresses);
    }

    /// <summary>
    /// Initialization logic that any derived type would use to set up the ServiceLocator provider.  Look to UnityServiceHostFactory to see how this is done, if implementing for 
    /// another IoC engine.
    /// </summary>
    protected abstract void RegisterDependencies();
}

Step 5: Concrete Implementation of DependencyInjectionServiceHostFactory

This step creates a concrete implementation of the DependencyInjectionServiceHostFactory class for your specific service. The only thing you have to do is implement the abstract method RegisterDependencies, which is how you will register your dependencies with Unity. The following code shows an example of a service for a Human Resources (HR) department.

/// <summary>
/// A custom service host factory that will register the dependencies with unity before the service is created.
/// </summary>
public class HRServiceHostFactory:DependencyInjectionServiceHostFactory
{
    protected override void RegisterDependencies()
    {
        DependencyFactory.Container.RegisterType(typeof(IHRRepository),typeof(HRRepository),new ContainerControlledLifetimeManager());
    }
}

Step 6: Configure the SVC File

The last step configures the SVC file to use the custom ServiceHostFactory that was created in step 5.

<%@ ServiceHost Language="C#" Debug="true" Service="ApprovalService.HRService" CodeBehind="HRService.cs"  Factory="ApprovalService.IoC.HRServiceHostFactory"%>

Obviously, creating a custom ServiceHostFactory requires a fair amount of code. However, only the code in steps five and six must be written more than once, after you create the reusable classes. Each new service needs only the concrete implementation of the DependencyInjectionServiceHostFactory, and the code that configures the SVC file.

There are two advantages of using a custom service host factory in your SVC files have over configuration files. First is that Unity does not have a tool for updating configuration files, so by registering your dependencies in code you will avoid having to manage a lot of difficult configuration. The second advantage is that you will know at compile time when one of your dependencies fail; whereas by using the configuration file approach, you would not know until runtime that one of your dependencies failed.

Show: