哪个是测试 Ninject 绑定的好方法?

Posted

技术标签:

【中文标题】哪个是测试 Ninject 绑定的好方法?【英文标题】:Which is a good approach to test Ninject bindings? 【发布时间】:2012-09-04 11:04:06 【问题描述】:

我们在所有项目中都使用 ninject,正如您所知道的,有时很难测试内核是否能够在执行时解析每种类型,因为有时当绑定和自动绑定的大小(通过ninject扩展)很高。

那么,我在这里要问的是,我如何知道我的内核在加载所有模块和绑定后,能够解析每种类型?你做任何类型的单元测试吗?或者您只是在执行时对应用程序进行验收测试?任何建议都会很棒:)

【问题讨论】:

你甚至不应该尝试它。你可以尝试,但它不会像你想象的那么有用,而且会花费更多的时间和精力。 blog.ploeh.dk/2011/12/21/TestingContainerConfigurations.aspx 相关:***.com/questions/9896344/… 我在这里快速搜索了我要问的内容,但没有结果,所以谢谢! 【参考方案1】:

根据欧文的回答,但这对我不起作用。 我所要做的就是将我的依赖项的新实例传递给一个新的内核实例,然后填充 Bindings 属性。

    [TestMethod]
    public void TestBindings
    
        var dependencies = new MyDependencies();
        var kernel = new StandardKernel(dependencies);

        foreach (var binding in dependencies.Bindings)
        
            kernel.Get(binding.Service);
        
    

我还选择使用kernal.Get,这样如果服务无法解析,则测试将因抛出错误而失败,并在消息中显示缺少的绑定

【讨论】:

【参考方案2】:

所以,我在这里要问的是,我如何知道我的内核在 加载所有模块和绑定,将能够解析每种类型? 您是否进行任何类型的单元测试?

我通过循环遍历模块中的每个绑定并检查内核是否可以为它们返回一些东西来测试这一点:

[Test]
public void AllModuleBindingsTest()

    var kernel = new StandardKernel(new MyNinjectModule())
    foreach (var binding in new MyNinjectModule().Bindings)
    
        var result = kernel.Get(binding.Service);
        Assert.NotNull(result, $"Could not get binding.Service");
    

【讨论】:

嗨欧文,我试过你的代码,但它没有返回 NinjectModule 的绑定列表【参考方案3】:

编写一个集成测试,通过循环访问应用程序中的所有根类型并从容器/内核请求它们来测试容器的配置。通过从容器中请求它们,您可以确定容器可以为您构建完整的对象图。

根类型是直接从容器请求的类型。大多数类型不是根类型,而是对象图的一部分(因为您应该很少从应用程序中回调容器)。当您测试根类型的创建时,您将立即测试该根类型的所有依赖项的创建,除非存在代理、工厂或其他可能延迟构建过程的机制。然而,延迟构建过程的机制确实指向其他根对象。您应该识别它们并测试它们的创建。

通过调用每种根类型的容器来防止自己进行大规模测试。相反,使用反射加载(如果可能)所有根类型并对其进行迭代。通过使用某种约定优于配置的方法,您可以避免 1. 为每个新的根类型更改测试,以及 2. 在您忘记为新的根类型添加测试时防止测试不完整。

这是一个 ASP.NET MVC 示例,其中您的根类型是控制器:

[TestMethod]
public void CompositionRoot_IntegrationTest()

    // Arrange
    CompositionRoot.Bootstrap();

    var mvcAssembly = typeof(HomeController).Assembly;

    var controllerTypes =
        from type in mvcAssembly.GetExportedTypes()
        where typeof(IController).IsAssignableFrom(type)
        where !type.IsAbstract
        where !type.IsGenericTypeDefinition
        where type.Name.EndsWith("Controller")
        select type;

    // Act
    foreach (var controllerType in controllerTypes)
    
        CompositionRoot.GetInstance(controllerType);
    

更新

塞巴斯蒂安·韦伯(Sebastian Weber)发表了一条有趣的评论,我想对此作出回应。

使用容器支持 (Func) 或 容器生成的工厂(如 Castle 的 Typed Factory Facility)? 你不会用那种测试抓住他们。那会给你一个 虚假的安全感。

我的建议是验证所有根类型。以延迟方式创建的服务实际上是根类型,因此应该明确地测试它们。这当然会迫使您密切监视对配置的更改,并在您检测到添加了无法使用您已有的约定优于配置测试进行测试的新根类型时添加测试。这还不错,因为没有人说使用 DI 和 DI 容器意味着我们可能会突然变得粗心。无论您是否使用 DI,创建好的软件都需要纪律。

当然,当您有许多延迟创建的注册时,这种方法会变得非常不方便。在这种情况下,您的应用程序的设计可能有问题,因为使用延迟创建应该是例外,而不是常态。另一件可能给您带来麻烦的事情是,当您的容器允许您通过将未注册的Func<T> 注册映射到() => container.GetInstance<T>() 委托来解决它们时。这听起来不错,但这迫使您超越容器注册来寻找根类型,并且更容易错过。由于延迟创建的使用应该是例外,因此最好使用显式注册。

另外请注意,即使您无法 100% 测试您的配置,但这并不意味着这会使测试配置毫无用处。我们无法自动测试 100% 的软件,应特别注意无法自动测试的软件/配置部分。例如,您可以将不可测试的部分添加到手动测试脚本中,然后手动测试它们。当然,您必须手动测试的次数越多,出错的可能性(也将会)越多,因此您应该尝试最大限度地提高配置的可测试性(就像您应该对所有软件所做的那样)。当你不知道你的测试是什么时,你当然会得到一种错误的安全感,但同样,这适用于我们专业的所有事情。

【讨论】:

我已经成功使用了这个方法 这似乎是一个不错的方法,因为它易于实施且快速。我一直在寻找这样的东西!非常感谢! 使用容器支持 (Func<T>) 或容器生成的工厂(如 Castle 的 Typed Factory Facility)延迟创建对象会怎样?你不会用那种测试抓住他们。那会给你一种虚假的安全感。 @Steven 你能发布你的 CompositionRoot 的样子吗? @AlfalfaStrange: Composition Root 是应用程序中组成(DI)对象图的地方;这是您在自己喜欢的 DI 容器中注册所有内容的地方。

以上是关于哪个是测试 Ninject 绑定的好方法?的主要内容,如果未能解决你的问题,请参考以下文章

Ninject之旅之九:Ninject上下文绑定(附程序下载)

Ninject

Ninject之旅之八:Ninject插件模型

Ninject - 如何在构造过程中识别哪个类抛出异常

如何迭代 Ninject StandardKernel 的已配置绑定以进行调试?

Ninject 激活 HttpConfiguration 时出错