Thursday, July 15, 2010

Adding mocking features to LinFu.Ioc

This post will show how to integrate a mocking library with the service container available in the LinFu framework.
We are going to kick this off by showing an example where the use of mock objects might be useful.

Imagine that we have two interfaces like the ones below.

public interface IServiceA
{
    int CalculateSomething();
}


public interface IServiceB
{
    int CalculateSomethingElse();
}

If we take a look at the implementation below we can see that ServiceA has a dependency to an IServiceB implementation.

public class ServiceA : IServiceA
{
    private IServiceB _serviceB;
    public ServiceA(IServiceB serviceB) 
    {
        _serviceB = serviceB;
    }

    public int CalculateSomething()
    {
        //Do something clever here....
        _serviceA.CalculateSomethingElse();
        //Do even more intelligent suff...
        return 42;
    }
}

It should be noted that dependency injection can be done in a variety of ways, but here it is done using constructor injection.

This means that when we request an instance of IServiceA the service container will also resolve the IServiceB implementation needed in the ServiceA constructor.

When writing our unit test for ServiceA, we might not be interested in whatever IServiceB does or even worse, it could represents a remote call not accessible to us when testing IServiceA. So we need to replace the default IServiceB implementation with a dummy implementation.

It is obvious how this can be done the manual way since this is such a naive example, but setting up mocking objects can be pretty complex if we need to install multiple objects.

It can also be the case that the dependency is created internally so that we do not have an easy way of replacing the implementation with a mocking instance.

So we need a way to tell the service container that whenever a request for a IServiceB instance is executed, it should return a mock instance.

It should also be noted here that I have used the Moq framework, but I guess this could very easily be adapted to other mocking frameworks as well.

Anyway, this is what I came up with:

ServiceContainer.StartMocking<IServiceB>();
var serviceA = ServiceContainer.GetService<IServiceA>();
var result = serviceA.CalculateSomething();
Assert.AreEqual(42,result);

We can see that creating a mock and configuring the service container is pretty simple.

ServiceContainer.StartMocking<IServiceB>();

This is all that we really need to do in order to replace the actual IServiceB implementation.

Buy what about those expectations you say?….

As it turns out the StartMocking method return a Mock<T> instance so that we can configure the mocking instance.

var mock =  ServiceContainer.StartMocking<IServiceB>();
mock.Expect(s => s.CalculateSomethingElse()).Returns(1);

So how is this actually possible? Well, we need to take a deeper look into LinFu.Ioc to answer that.

LinFu.Ioc actually offers several ways of hooking into the process of resolving service types and one is by implementing the IPreProcessor interface.

An IPreProcessor implementation allows us to inspects the service request and optionally replace its default factory.

/// <summary>
/// Inspects the requested service type and returns a mocked service instance if 
/// mocking has been enabled for the service type.     
/// </summary>
/// <seealso cref="ContainerExtensions"/>
public class MockPreprocessor : IPreProcessor
{
    private readonly IList<MockServiceInfo> _mockedServices;

    /// <summary>
    /// Initializes a new instance of the <see cref="MockPreprocessor"/>
    /// </summary>
    /// <param name="mockedServices"></param>
    public MockPreprocessor(IList<MockServiceInfo> mockedServices)
    {
        _mockedServices = mockedServices;
    }

    /// <summary>
    /// Inspects the service request and return a mocked service if available.
    /// </summary>        
    public void Preprocess(IServiceRequest request)
    {
        var serviceName = string.IsNullOrEmpty(request.ServiceName) ? null : request.ServiceName;
        
        var mockedServiceInfo =
            _mockedServices.Where(
                ms => ms.ServiceType == request.ServiceType && ms.ServiceName == serviceName).FirstOrDefault
                ();
        
        if (mockedServiceInfo != null)            
            request.ActualFactory = new FunctorFactory(r => mockedServiceInfo.Mock.Object);
    }
}

The MockPreProcessor takes a list of MockServiceInfo instances that basically tells the preprocessor what types for which to return mock objects.

/// <summary>
/// Contains details about the service type being mocked
/// </summary>
public class MockServiceInfo
{
    /// <summary>
    /// Gets or sets the <see cref="Mock"/> instance.
    /// </summary>
    public Mock Mock { get; set; }
    
    /// <summary>
    /// Gets or sets the service type being mocked.
    /// </summary>
    public Type ServiceType { get; set; }
            
    /// <summary>
    /// Gets or sets the name of the service being mocked.
    /// </summary>
    public string ServiceName { get; set; }
}

Now that we have all the basic infrastructure in place, we need some extensions methods that defines the API for the Moq integration.

/// <summary>
/// Contains extensions methods to integrate the Moq mocking library with the LinFu.Ioc framework.
/// </summary>
/// <seealso cref="http://code.google.com/p/moq/"/>
public static class ContainerExtensions
{
    private static readonly IList<MockServiceInfo> _mockedServices = new List<MockServiceInfo>();

    /// <summary>
    /// Creates a new <see cref="Mock{T}"/> instance and instructs the <see cref="IServiceContainer"/> 
    /// to return the <see cref="Mock.Object"/> instead of the actual service instance.
    /// </summary>
    /// <typeparam name="T">The type of service being mocked.</typeparam>
    /// <param name="serviceContainer">The <see cref="IServiceContainer"/> that will handle the mocked service request.</param>
    /// <returns><see cref="Mock{T}"/></returns>
    public static Mock<T> StartMocking<T>(this IServiceContainer serviceContainer) where T:class
    {            
        return StartMocking<T>(serviceContainer, null);            
    }

    /// <summary>
    /// Creates a new <see cref="Mock{T}"/> instance and instructs the <see cref="IServiceContainer"/> 
    /// to return the <see cref="Mock.Object"/> instead of the actual service instance.
    /// </summary>
    /// <typeparam name="T">The type of service being mocked.</typeparam>
    /// <param name="serviceContainer">The <see cref="IServiceContainer"/> responsible for creating the mocked service.</param>
    /// <param name="serviceName">The name of the service being mocked.</param>
    /// <returns><see cref="Mock{T}"/></returns>
    public static Mock<T> StartMocking<T>(this IServiceContainer serviceContainer, string serviceName) where T:class
    {
        InstallMockPreProcessor(serviceContainer);

        return CreateMock<T>(serviceName);
    }

    private static Mock<T> CreateMock<T>(string serviceName) where T:class
    {                        
        var mock = new Mock<T>();            
        var mockServiceInfo = new MockServiceInfo();
        mockServiceInfo.Mock = mock;
        mockServiceInfo.ServiceName = string.IsNullOrEmpty(serviceName) ? null : serviceName;
        mockServiceInfo.ServiceType = typeof (T);
        _mockedServices.Add(mockServiceInfo);
        return mock;
    }

    private static void InstallMockPreProcessor(IServiceContainer serviceContainer)
    {
        if (!serviceContainer.PreProcessors.Any(p => p.GetType() == typeof (MockPreprocessor)))
            serviceContainer.PreProcessors.Add(new MockPreprocessor(_mockedServices));
    }


    /// <summary>
    /// Stops mocking the requested service type.
    /// </summary>
    /// <typeparam name="T">The service type currently being mocked.</typeparam>
    /// <param name="serviceContainer">The <see cref="IServiceContainer"/> currenly responsible for creating the mocked service.</param>        
    public static void StopMocking<T>(this IServiceContainer serviceContainer) where T : class
    {
        StopMocking<T>(serviceContainer, null);
    }


    /// <summary>
    /// Stops mocking the requested service type.
    /// </summary>
    /// <typeparam name="T">The service type currently being mocked.</typeparam>
    /// <param name="serviceContainer">The <see cref="IServiceContainer"/> currenly responsible for creating the mocked service.</param>
    /// <param name="serviceName">The name of the service curretly being mocked.</param>
    public static void StopMocking<T>(this IServiceContainer serviceContainer, string serviceName) where T : class
    {
        serviceName = string.IsNullOrEmpty(serviceName) ? null : serviceName;
        var mockServiceInfo =
            _mockedServices.Where(ms => ms.ServiceType == typeof (T) && ms.ServiceName == serviceName).
                FirstOrDefault();

        if (mockServiceInfo != null)
            _mockedServices.Remove(mockServiceInfo);
    }

    /// <summary>
    /// Stops mocking all services.
    /// </summary>
    /// <param name="serviceContainer">The <see cref="IServiceContainer"/> responsible for creating mocked services.</param>
    public static void StopMocking(this IServiceContainer serviceContainer)
    {
        _mockedServices.Clear();
    }

}

There are also methods for actually stop mocking types in the service container and that might be useful if you have different needs in various tests targeting the same service container instance.

Download the source here.

 

Happy mocking!!!

 

 

 

 

 

 

 
  

No comments: