Test Your IoC Container

There are doubts about if you should test registrations in you IoC container. I have been intimidated by this blog-post for a while and did not touch container for writing our tests. However, we did have a problem in the past, when DI container could not create one of our controllers. And we have lost days trying to chase the problem – there was no meaningful error message, because the real exception was swallowed somewhere in transit. It turned out that we have violated DI principal of a simple constructor: “Do nothing in constructor”. I have blogged about this before.

Today I would like to show how we test our IoC container to check if it can create all our controllers. Because controllers are immediate aggregate roots that are used by an end user. And controllers in MVC are the final consumer in the dependency chain. So if you likely to hit a problem in a chain of dependencies, it is likely that you will catch it by creating all the controllers.

Of course, if you use IoC container as a service locator or Mediator pattern you will have to test container separately for the types you get out from container there (mostly Command Handlers and Query Handlers).

So enough talking, here is my test for Autofac container. You can adjust the code for the container you need – should be pretty simple.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using Autofac;
using Autofac.Core;
using NUnit.Framework;


/// <summary>
/// Verify that all controllers can be created by autofac
/// </summary>
[Category("database")]
public class AutofacControllersTests
{
    [TestFixtureSetUp]
    public void FixtureSetUp()
    {
        // StubConfiguration is a class with our test values for configuration.
        // also contain connection string for database
        ConfigurationContext.Current = new StubConfiguration(); 

        // probably here you will need to set up your database to be 
        // compliant with your EF DbContext class, because ultimately our controllers
        // reach into database and DbContext will be resolved. And for that it'll require 
        // a valid connection string
    }


    [Test]
    public void AutofacContainer_Always_ResolvesAllControllers()
    {
        var errors = new List<string>();

        // HttpSimulator simulates a request. See more here: http://tech.trailmax.info/2013/11/faking-http-context-for-your-unit-tests/
        using (new HttpSimulator().SimulateRequest())
        {
            //Arrange   
            // this builds our container
            var container = ...// here build your container

            var controllerTypes = GetControllerTypes();

            // Act
            foreach (var controllerType in controllerTypes)
            {
                try
                {
                    container.Resolve(controllerType);
                }
                catch (Exception exception)
                {
                    var resolutionException = exception as DependencyResolutionException;
                    if (resolutionException != null)
                    {
                        errors.Add(resolutionException.Message);
                    }
                    else
                    {
                        errors.Add(exception.ToString());
                    }
                }
            }


            // Assert
            var separator = String.Format("{0}{0}------------------------------------{0}", Environment.NewLine);
            var finalMessage = separator + String.Join(separator, errors);
            Assert.IsEmpty(errors, finalMessage);
        }
    }

    public static IEnumerable<Type> GetControllerTypes()
    {
    // we are using T4MVC and we don't want to test generated code.
    // hence exclude controllers starting with T4MVC
        return Assembly.GetAssembly(typeof(MvcApplication))
            .GetTypes()
            .Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(Controller)))
            .Where(t => t.Namespace != null && !t.Name.Contains("T4MVC"))   
            .ToList();
    }
}