Avoiding or UnitTest accidental use of a specific singleton type instead of abstraction with StructureMap

I recently had the following error in my code that made me debug forever. I would like to introduce an instance based on its interface as follows:

MovementController(IMotorController motorController) 

However, I accidentally used a specific type as follows:

 MovementController(MotorController motorController) 

The project is still built and working fine until I tried to access motorController from an instance of MovementController . Since the base implementation of IMotorController accesses hardware, it must be single-point or my lock code. However, since I had other classes with IMotorController introduced, now I had two instances of motorController in my object graph, which both accessed the hardware through a serial connection. This caused a runtime error at a much lower level, which made me permanently debug and find the true cause.

How can I avoid this type of error or write unit test for my StructureMap registry to catch this subtle error?

+5
source share
4 answers

You can easily verify this with a static analysis tool such as NDepend. With it, you will simply search for types that were controllers, and then check their constructors and warn if you find any constructor parameters that were not interface types.


To clarify Steve’s answer, you can write a code rule that might look like this: (using NDepend the C # LINQ query code rule with the warnif count > 0 prefix warnif count > 0 )

 // <Name>Don't use MotorController, use IMotorController instead</Name> warnif count > 0 from m in Application.Methods where m.IsUsing ("NamespaceA.MotorController ") && m.ParentType.FullName != "NamespaceB.ClassThatCanUseMotorController " select m 

A rule can be easily cleared if there are zero or many ClassThatCanUseMotorController .

+1
source

Ok So, the solution that I came up with for my Unit Test is to get all the instances that implement IMotorController and claim that their number is 1 :

  var motorControllerInstances = container.GetAllInstances<IMotorController>().Select(x => x); // cast enumerable to List using Linq Assert.True(motorControllerInstances.Count == 1); 

Not sure if this is the most elegant way, but it works.

Update 1: This code does not catch the error I had. I'm still looking for the right answer to my problem.

Update 2: I'm getting closer. This will at least catch if you accidentally register a specific type of corresponding interface. However, it does not check if an instance of it was actually created.

  var allInterfaceInstances = dicFixture.result.Model.GetAllPossible<IMotorController>(); Assert.True(allInterfaceInstance.Count() == 1); 
0
source

The safest solution is to check at runtime that only one instance of MotorController . For example, you can count the number of instances of MotorController with a static counter variable:

 public class MotorController : IMotorController { private static bool instantiated; public MotorController(...) { if (instantiated) throw new InvalidOperationException( "MotorController can only be instantiated once.") ... instantiated = true; } ... } 

I usually consider this poor design because if the class is used as a singleton or not, this is something that should only take care of the dependency injection infrastructure. Also note that this is not thread safe.

0
source

When trying to stick to D in SOLID

The principle of dependency inversion, where "Depend on abstractions" is needed. Independent of nodules

for the project. In this case, Asp.Net-MVC5, I needed a way to make sure that all the controllers (MVC and WebAPI2) follow this pattern, where they are independent of nodules.

The initial idea was taken from an article I read, where a unit test was created to scan all the controllers to make sure they have explicit permission. I applied similar thinking in verifying that all controllers have abstraction-dependent constructors.

 [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, let's give the following example. (Note: I adapted this for MVC)

 public class MovementController : Controller { public MovementController(MotorController motorController) { //... } } public interface IMotorController { //... } public class MotorController : IMotorController { //... } 

unit test will fail ...

 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 generic type that needs to be searched with IController and ApiController .

There is room for improvement in the test, but this should be a good starting point for you.

0
source

All Articles