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

时间:2012-09-11 13:38:14

标签: c# dependency-injection ninject ioc-container

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

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

3 个答案:

答案 0 :(得分:19)

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

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

通过调用每个根类型的容器,防止自己进行一次巨大的测试。相反,使用反射加载(如果可能)所有根类型并迭代它们。通过使用某种约定优于配置方法,您可以避免必须更改每个新根类型的测试,以及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);
    }
}

<强>更新

塞巴斯蒂安·韦伯发表了一篇有趣的评论,我想作出回应。

  

使用容器备份(Func)或   集装箱生成工厂(如Castle's Typed Factory Facility)?   你不会接受这种测试。那会给你一个   错误的安全感。

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

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

另请注意,即使您无法测试100%的配置,这并不意味着这使得测试配置毫无用处。我们无法自动测试100%的软件,应特别注意无法自动测试的软件/配置部分。例如,您可以将不可测试的部件添加到手动测试脚本中,并手动测试它们。当然,您需要手动测试的越多,就越可能(并且将会)出错,因此您应该尝试最大化配置的可测试性(就像您应该对所有软件一样)。当你不知道你的测试是什么时,你当然会得到一种虚假的安全感,但这又适用于我们职业的一切。

答案 1 :(得分:4)

  

所以,我在这里问的是,我怎么能知道我的内核   加载所有模块和绑定,将能够解决每种类型?   你做过任何单元测试吗?

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

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

答案 2 :(得分:1)

基于Owen的回答,但这对我来说并不起作用。 我要做的就是将依赖项的新实例传递到新的内核实例中,然后填充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,以便如果无法解决该服务,则测试将由于抛出的错误而失败,并在消息中显示缺少的绑定