2016-08-05 57 views
5

我最近有我的代码中的以下错误,使我永远调试。我想基于其界面类似这样的注入一个实例:如何避免或UnitTest意外使用混凝土单体类型而不是其抽象与StructureMap

MovementController(IMotorController motorController) 

但是我不小心使用的具体类型是这样的:

MovementController(MotorController motorController) 

该项目还建立并运行良好,直到我试图访问来自MovementController实例的motorController。由于IMotorController的底层实现访问硬件,它必须是单例或我的锁代码。但是,由于我有其他类注入了IMotorController,我现在在我的对象图中有两个实例MotorController,它们都通过串行连接访问硬件。这导致了一个错误,在运行时在一个更低的水平,这使我永远调试,并找到真正的原因。

如何避免此类错误或为我的StructureMap注册表编写单元测试以捕获此细微错误?

+0

让我们来看看是我的理解:你想一个单元测试来检查你的控制器只有接口构造函数的参数?这是什么样的项目? MVC,winform,wpf ...? – Nkosi

回答

0

好的。所以,我想出了我的单元测试的解决方案,是让实现IMotorController的所有实例,并声称他们的计数等于1

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

不知道这是最优雅的方式,但它似乎工作。

更新1: 此代码赶上我有错误。我仍然在为我的问题寻找正确答案。

更新2:我越来越近了。如果您不小心已注册这是至少可以捕获相应接口的具体类型。但是,它似乎没有检查它的实例是否实际构建。

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

这个问题对我来说很有意思。我有几个问题想知道我的解决方案是否可以应用于您的情况。我不熟悉结构映射,但是您知道是否有方法可以识别设置中的所有映射以及应用接口的类? – Nkosi

+0

@Nkosi不幸的是,我不知道是否或如何可能。但是,对我来说,验证我的配置也是非常有趣的。我一定会在未来看到这一点。然而,我确实找到了一些SO帖子,在那里提到海报,他发现的最佳方式是使该字段被公开存储并直接验证该字段。这看起来不太好,但海报确实说他没有找到更好的方法。不幸的是我没有链接,不知道他谈论的是哪个StructureMap版本。 – packoman

+0

好的。以及我使用DI的ASP.Net MVC项目,并希望确保我的所有控制器都只被注入接口或抽象类。没有具体的实现。所以我写了一个单元测试,检查所有控制器(MVC和WEB API),以确保它们的构造函数没有注入控制器的类,只有抽象。所以我可以通过它们的'IController'和'ApiController'继承来识别这些类,然后检查它们的构造函数。从你的例子我无法知道你有什么类型的项目。 – Nkosi

0

最安全的解决方案是在运行时检查仅创建MotorController的一个实例。例如,你可以指望的MotorController实例的数量与静态计数器变量:

public class MotorController : IMotorController 
{ 
    private static bool instantiated; 

    public MotorController(...) 
    { 
     if (instantiated) 
      throw new InvalidOperationException(
       "MotorController can only be instantiated once.") 

     ... 

     instantiated = true; 
    } 

    ... 
} 

我通常认为这是不好的设计,因为不管是一类被用作单身与否事只有依赖注入框架应该关心。还要注意,这不是线程安全的。

0

在努力坚持dSOLID

依赖倒置原则,在 一个应“取决于抽象。不依赖于结核

为一个项目。在这种情况下,我想要一种方法来确保所有控制器(MVC和WebAPI2)都遵循这种模式,而不依赖于结核。

最初的想法从文章来我读了其中一个单元测试创​​建扫描所有控制器,以确保他们有明确的授权规定。我在检查所有控制器都有依赖于抽象的构造函数时采用了类似的思路。

[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))); 
     } 
    } 
} 

因此给出以下示例。 (请注意我适应这MVC)

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

public interface IMotorController { 
    //... 
} 

public class MotorController : IMotorController { 
    //... 
} 

与...

Result Message: Assert.Fail failed. 
Type depends on concretion instead of its abstraction. 
1 Found : 
MovementController(MotorController motorController) 

这为我工作,因为我有一个常见的类型,以寻找与IControllerApiController单元测试会失败。

有空间上的测试改进,但应该是你一个很好的起点。

1

你可以很容易地检查这个使用类似NDepend的静态分析工具。有了它,你只需寻找控制器的类型,然后检查它们的构造函数,并警告你是否发现任何不是接口类型的构造函数参数。


只是为了细化史蒂夫的回答,您可以编写代码规则可能看起来像:(与NDepend的代码规则与warnif count > 0前缀一个C# LINQ query

// <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 

规则可以细化很容易如果有零个或多个ClassThatCanUseMotorController