In trying to adhere to the D in SOLID
Dependency inversion principle where
one should “Depend upon Abstractions. Do not depend upon concretions
for a project. In this case Asp.Net-MVC5, I wanted a way to make sure that all controllers (MVC and WebAPI2) were following this pattern where they were not dependent on concretions.
The original idea came from an article I had read where a unit test was created to scan all controllers to make sure that they had explicit authorization defined. I applied a similar thinking in checking that all controllers had constructors that depended on abstractions.
[TestClass]
public class ControllerDependencyTests : ControllerUnitTests {
[TestMethod]
public void All_Controllers_Should_Depend_Upon_Abstractions() {
var controllers = UnitTestHelper.GetAssemblySources() //note this is custom code to get the assemblies to reflect.
.SelectMany(assembly => assembly.GetTypes())
.Where(t => typeof(IController).IsAssignableFrom(t) || typeof(System.Web.Http.Controllers.IHttpController).IsAssignableFrom(t));
var constructors = controllers
.SelectMany(type => type.GetConstructors())
.Where(constructor => {
var parameters = constructor.GetParameters();
var result = constructor.IsPublic
&& parameters.Length > 0
&& parameters.Any(arg => arg.ParameterType.IsClass && !arg.ParameterType.IsAbstract);
return result;
});
// produce a test failure error mssage if any controllers are uncovered
if (constructors.Any()) {
var errorStrings = constructors
.Select(c => {
var parameters = string.Join(", ", c.GetParameters().Select(p => string.Format("{0} {1}", p.ParameterType.Name, p.Name)));
var ctor = string.Format("{0}({1})", c.DeclaringType.Name, parameters);
return ctor;
}).Distinct();
Assert.Fail(String.Format("\nType depends on concretion instead of its abstraction.\n{0} Found :\n{1}",
errorStrings.Count(),
String.Join(Environment.NewLine, errorStrings)));
}
}
}
So given the following example. (Note I adapted this to MVC)
public class MovementController : Controller {
public MovementController(MotorController motorController) {
//...
}
}
public interface IMotorController {
//...
}
public class MotorController : IMotorController {
//...
}
the unit test would fail with ...
Result Message: Assert.Fail failed.
Type depends on concretion instead of its abstraction.
1 Found :
MovementController(MotorController motorController)
This worked for me because I had a common type to look for with the IController
and ApiController
.
There is room for improvement on the test but is should be a good starting point for you.