C# 哪种方法是测试Ninject绑定的好方法?

C# 哪种方法是测试Ninject绑定的好方法?,c#,dependency-injection,ninject,ioc-container,C#,Dependency Injection,Ninject,Ioc Container,我们在所有项目中都使用ninject,正如您所知,有时很难测试内核是否能够在执行时解析每种类型,因为有时绑定和自动绑定(通过ninject扩展)的数量很大时会丢失控制 所以,我在这里要问的是,我如何知道我的内核在加载所有模块和绑定之后,将能够解析每种类型?你做过单元测试吗?或者您只是在执行时对应用程序进行验收测试?任何建议都很好:)编写一个集成测试,通过循环应用程序中的所有根类型并从容器/内核请求它们来测试容器的配置。通过从容器中请求它们,您可以确保容器可以为您构建完整的对象图 根类型是直接从容

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


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

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

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

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

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

[TestMethod]
public void CompositionRoot_IntegrationTest()
{
//安排
CompositionRoot.Bootstrap();
var mvcAssembly=typeof(HomeController).Assembly;
变量控制器类型=
来自mvcAssembly.GetExportedTypes()中的类型
其中typeof(IController).IsAssignableFrom(type)
在哪里!输入IsAbstract
其中!type.IsGenericTypeDefinition
其中type.Name.EndsWith(“控制器”)
选择类型;
//表演
foreach(controllerTypes中的var controllerType)
{
CompositionRoot.GetInstance(controllerType);
}
}
更新

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


使用容器备份(Func)或 容器生成工厂(如Castle的类型化工厂设施)? 你不会用那种测试抓住他们的。那会给你一个机会 虚假的安全感

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

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

还要注意的是,即使您不能100%测试您的配置,这并不意味着测试配置是无用的。我们不能100%自动测试我们的软件,应该特别注意不能自动测试的软件/配置部分。例如,您可以将不稳定的部分添加到手动测试脚本中,并手动测试它们。当然,手工测试的次数越多,出错的可能性(也会)越大,因此您应该尽量提高配置的可测试性(就像您对所有软件所做的那样)。当你不知道你的测试是什么的时候,你当然会有一种虚假的安全感,但是,这同样适用于我们职业中的每一件事

所以,我在这里要问的是,在 加载所有模块和绑定,是否能够解析每种类型? 你做过单元测试吗

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

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

根据欧文的回答,但这对我来说不起作用。 我所要做的就是将依赖项的新实例传递到一个新的内核实例中,然后填充
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
,这样,如果服务无法解决,那么测试将因抛出的错误而失败,并在消息中显示丢失的绑定

我成功地使用了这种方法。这似乎是一种很好的方法,因为它易于快速实现。我在找这样的东西!非常感谢你!使用容器备份(
Func
)或conta延迟对象创建如何