OdeToCode IC Logo

Enforcing a Base Controller

Tuesday, June 29, 2010

In the previous post we talked about using a base controller class in ASP.NET MVC. The truly paranoid will worry about new developers on the team who don't know the rules and don't use the base class. One automated solution to this problem is to write a unit test to enforce the rule.

[TestMethod]
public void AllControllersDeriveFromFootballController()
{
    var badControllers =
        GetConcreteTypesAssignableTo<IController>()
       .Where(TypeIsNotDerivedFrom<FootballController>);

    if(badControllers.Any())
    {
        var controllerNames = AggregateTypeNames(badControllers);
        

        Assert.Fail("Controllers are out of line: {0}", 
                     controllerNames);
    }            
}

The above code uses a few helper methods:

IEnumerable<Type> GetConcreteTypesAssignableTo<T>()
{
    return
        Assembly
            .GetExecutingAssembly()
            .GetReferencedAssemblies()
            .Select(name => Assembly.Load(name.FullName))
            .SelectMany(assembly => assembly.GetTypes())
            .Where(type => typeof (T).IsAssignableFrom(type))
            .Where(type => !type.IsAbstract);
}

bool TypeIsNotDerivedFrom<TBase>(Type type)
{
    return !(typeof (TBase).IsAssignableFrom(type));
}

string AggregateTypeNames(IEnumerable<Type> types)
{
    return
        types
            .Select(type => type.Name)
            .Aggregate(new StringBuilder(),
                       (sb, name) => sb.Append(name + " "))
            .ToString();
}

Of course the test isn't so simple if controllers might use different base classes (think asynchronous controllers), and I'm sure some people question the value of a test like this in any case. Right?