unit-testing Containers / DI Frameworks


Example

Whilst extracting dependencies out of your code so that they can be injected makes your code easier to test, it pushes the problem further up the hierarchy and can also result in objects that are difficult to construct. Various dependency injection frameworks / Inversion of Control Containers have been written to help overcome this issue. These allow type mappings to be registered. These registrations are then used to resolve dependencies when the container is asked to construct an object.

Consider these classes:

public interface ILogger {
    void Log(string message);
}

public class ConcreteLogger : ILogger
{
    public ConcreteLogger()
    {
        // ...
    }
    public void Log(string message)
    {
        // ...
    }
}
public class SimpleClass
{
    public SimpleClass()
    {
        // ...
    }
}

public class SomeProcessor
{
    public SomeProcessor(ILogger logger, SimpleClass simpleClass)
    {
        // ...
    }
}

In order to construct SomeProcessor, both an instance of ILogger and SimpleClass are required. A container like Unity helps to automate this process.

First the container needs to be constructed and then mappings are registered with it. This is usually done only once within an application. The area of the system where this occurs is commonly known as the Composition Root

// Register the container
var container = new UnityContainer();

// Register a type mapping.  This allows a `SimpleClass` instance
// to be constructed whenever it is required.
container.RegisterType<SimpleClass, SimpleClass>();

// Register an instance.  This will use this instance of `ConcreteLogger` 
// Whenever an `ILogger` is required.
container.RegisterInstance<ILogger>(new ConcreteLogger());

After the container is configured, it can be used to create objects, automatically resolving dependencies as required:

var processor = container.Resolve<SomeProcessor>();