简体   繁体   English

Ninject内核绑定覆盖

[英]Ninject kernel binding overrides

I'm just wondering what the best practice is for rewiring the bindings in a kernel. 我只是想知道在内核中重新绑定绑定的最佳做法是什么。

I have a class with a kernel and a private class module with the default production bindings. 我有一个带有内核的类和一个带有默认生产绑定的私有类模块。

For tests I want to override these bindings so I can swap in my Test Doubles / Mocks objects. 对于测试,我想覆盖这些绑定,以便我可以交换我的Test Doubles / Mocks对象。

does

MyClass.Kernel.Load(new InlineModule(m=> m.Bind<IDepend>().To<TestDoubleDepend>()))

override any existing bindings for IDepend? 覆盖IDepend的任何现有绑定?

I try to use the DI kernel directly in my code as little as possible, instead relying on constructor injection (or properties in select cases, such as Attribute classes). 我尝试尽可能少地在我的代码中直接使用DI内核,而不是依赖于构造函数注入(或选择情况下的Attribute ,例如Attribute类)。 Where I must, however, I use an abstraction layer, so that I can set the DI kernel object, making it mockable in unit tests. 但是,我必须使用抽象层,以便我可以设置DI内核对象,使其在单元测试中可模拟。

For example: 例如:

public interface IDependencyResolver : IDisposable
{
    T GetImplementationOf<T>();
}

public static class DependencyResolver
{
    private static IDependencyResolver s_resolver;

    public static T GetImplementationOf<T>()
    {
        return s_resolver.GetImplementationOf<T>();
    }

    public static void RegisterResolver( IDependencyResolver resolver )
    {
        s_resolver = resolver;
    }

    public static void DisposeResolver()
    {
        s_resolver.Dispose();
    }
}

Using a pattern like this, you can set the IDependencyResolver from unit tests by calling RegisterResolver with a mock or fake implementation that returns whatever objects you want without having to wire up full modules. 使用这样的模式,您可以通过使用模拟或伪实现调用RegisterResolver来设置单元测试中的IDependencyResolver ,该实现返回您想要的任何对象,而无需连接完整模块。 It also has a secondary benefit of abstracting your code from a particular IoC container, should you choose to switch to a different one in the future. 如果您将来选择切换到另一个容器,它还具有从特定IoC容器中抽象代码的第二个好处。

Naturally, you'd also want to add additional methods to IDependencyResolver as your needs dictate, I'm just including the basics here as an example. 当然,您还需要根据需要向IDependencyResolver添加其他方法,我只是将这里的基础知识作为示例。 Yes, this would then require that you write a super simple wrapper around the Ninject kernel which implements IDependencyResolver as well. 是的,这将要求您在Ninject内核周围编写一个超级简单的包装器,它也实现了IDependencyResolver

The reason you want to do this is that your unit tests should really only be testing one thing and by using your actual IoC container, you're really exercising more than the one class under test, which can lead to false negatives that make your tests brittle and (more importantly) shake developer faith in their accuracy over time. 你想要这样做的原因是你的单元测试应该只测试一件事,并且通过使用你的实际IoC容器,你实际上比一个被测试的类更多地运动,这可能导致错误的负面因素,使你的测试脆弱和(更重要的是)随着时间推移开发人员对其准确性的信心。 This can lead to test apathy and abandonment since it becomes possible for tests to fail but the software to still work correctly ("don't worry, that one always fails, it's not a big deal"). 这可能导致测试冷漠和放弃,因为测试失败但软件仍然可以正常工作(“不用担心,一个总是失败,这不是什么大不了的事”)。

I am just hoping something like this works 我只是希望这样的事情有效

        var kernel = new StandardKernel(new ProductionModule(config));
        kernel.Rebind<ITimer>().To<TimerImpl>().InSingletonScope();

where ProductionModule is my production bindings and I override by calling Rebind in the specific test case. ProductionModule是我的生产绑定,我通过在特定测试用例中调用Rebind来覆盖。 I call rebind on the few items I rebind. 我在重新绑定的几件物品上打电话重新绑定。

ADVANTAGE: If anyone adds new bindings to production module, I inherit them so it won't break in this fashion which can be nice. 优点:如果有人向生产模块添加了新的绑定,我会继承它们,因此它不会以这种方式破坏,这可能很好。 This all works in Guice in java...hoping it works here too. 这一切都适用于Java中的Guice ...希望它也可以在这里工作。

What I tend to do is have a separate test project complete with it's own bindings -- I'm of course assuming that we're talking about unit tests of some sort. 我倾向于做一个单独的测试项目完成它自己的绑定 - 我当然假设我们正在谈论某种单元测试。 The test project uses its own kernel and loads the module in the test project into that kernel. 测试项目使用自己的内核并将测试项目中的模块加载到该内核中。 The tests in the project are executed during CI builds and by full builds executed from a build script, though the tests are never deployed into production. 项目中的测试在CI构建期间执行,并通过构建脚本执行完整构建,但测试从未部署到生产中。

I realize your project/solution setup may not allow this sort of organization, but it seems to be pretty typical from what I've seen. 我意识到你的项目/解决方案设置可能不允许这种组织,但从我看到的看起来似乎很典型。

Peter Mayer's approach shuoul be useful for Unit Test, but IMHO, isn't it easier to just inject manually a Mock using a constructor/property injection? Peter Mayer的方法对于单元测试很有用,但恕我直言,使用构造函数/属性注入手动注入Mock是不是更容易?

Seems to me that using specific bindings for a test project will be more useful for other kind of test (integration, functional) but even in that case you surely need to change the bindings depending on the test. 在我看来,对测试项目使用特定绑定对于其他类型的测试(集成,功能)更有用,但即使在这种情况下,您肯定需要根据测试更改绑定。

My approach is some kind of a mix of kronhrbaugh and Hamish Smith, creatin a "dependency resolver" where you can register and unregister the modules to be used. 我的方法是某种kronhrbaugh和Hamish Smith的混合,创建了一个“依赖解析器”,你可以注册和注销要使用的模块。

I would add a constructor to MyClass that accepts a Module. 我会向MyClass添加一个接受模块的构造函数。
This wouldn't be used in production but would be used in test. 这不会用于生产,但会用于测试。
In the test code I would pass a Module that defined the test doubles required. 在测试代​​码中,我将传递一个模块,该模块定义了所需的测试双精度数。

For a project I am working on, I created separate modules for each environment (test, development, stage, production, etc.). 对于我正在进行的项目,我为每个环境(测试,开发,阶段,生产等)创建了单独的模块。 Those modules define the bindings. 这些模块定义了绑定。

Because dev, stage and production all use many of the same bindings, I created a common module they all derive from. 因为dev,stage和production都使用了许多相同的绑定,所以我创建了一个共同的模块。 Each one then adds it environment specific bindings. 然后每个人都添加环境特定的绑定。

I also have a KernelFactory, that when handed an environment token will spool up an IKernel with the appropriate modules. 我还有一个KernelFactory,当传递一个环境令牌时,它将使用适当的模块对一个IKernel进行假脱机。

This allows me to switch my environment token which will in turn change all of my bindings automatically. 这允许我切换我的环境令牌,这反过来会自动更改我的所有绑定。

But if this is for unit testing, I agree with the above comments that a simple constructor allowing manual binding is the way to go as it keeps Ninject out of your tests. 但是如果这是用于单元测试的话,我同意上面的评论,即允许手动绑定的简单构造函数是一种方法,因为它使Ninject远离你的测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM