2009-08-11 115 views
5

我只是想知道最好的做法是重新连接内核中的绑定。Ninject内核绑定覆盖

我有一个内核类和一个私有类模块与默认生产绑定类。

对于测试我想重写这些绑定,所以我可以交换我的测试双打/ Mocks对象。

确实

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

覆盖了IDepend任何现有的绑定?

回答

0

我会添加一个构造函数给接受模块的MyClass。
这不会用于生产,但会用于测试。
在测试代码中,我将传递一个定义所需测试双精度的模块。

1

我倾向于做的是有一个单独的测试项目与它自己的绑定完成 - 我当然假设我们正在谈论某种单元测试。测试项目使用自己的内核,并将测试项目中的模块加载到该内核中。项目中的测试在CI构建过程中执行,并通过构建脚本执行完整构建来执行,尽管测试从未部署到生产环境中。

我意识到你的项目/解决方案设置可能不允许这种类型的组织,但它似乎是我见过的非常典型的。

5

我尝试尽可能少地直接在我的代码中使用DI内核,而不是依赖构造函数注入(或在特定情况下的属性,例如Attribute类)。然而,在我必须使用抽象层的情况下,我可以设置DI内核对象,使其在单元测试中可以嘲弄。

例如:

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

使用这样的模式,您可以通过调用RegisterResolver与模拟或假的实现,它返回任何你想要的对象,而不必线了全模块设置为从单元测试的IDependencyResolver 。它还具有从特定IoC容器中提取代码的次要好处,如果您选择在将来切换到另一个代码。

当然,您还需要根据需要添加更多方法到IDependencyResolver,我只是将这里的基础知识作为示例。是的,这会要求你在Ninject内核上编写一个超级简单的包装器,它也实现了IDependencyResolver

你想这样做的原因是,你的单元测试应该只是测试一件事情,并且通过使用你的实际IoC容器,你实际上比被测试的一个类运行得更多,这会导致错误的否定这会让你的测试变得脆弱,并且(更重要的是)随着时间的推移,让开发人员相信他们的准确性。这可能会导致测试的冷漠和放弃,因为测试失败,但软件仍然可以正常工作(“别担心,总是失败,这不是什么大问题”)。

+0

对于解析器模式+1,但是,真的尝试在使用IDepend的MyClass中使用构造器注入。这样,你的单元测试甚至不需要你的IoC容器。 – 2010-09-23 17:16:43

+0

为什么不只是做一些更简单的像重写绑定一样... var kernel = new StandardKernel(new ProductionModule(config)); ()。 kernel.Rebind ().To ().InSingletonScope(); – 2011-12-28 17:52:51

+0

您可以覆盖绑定,但是您的测试也会测试您的DI容器以及待测试的单元。在某些情况下,这不是什么大不了的事情,但正如我在答案中所概括的那样,这可能会导致错误的否定并削弱测试套件的信任。 – krohrbaugh 2012-01-04 19:52:38

1

Peter Mayer的方法shuoul对于单元测试是有用的,但是恕我直言,使用构造函数/属性注入手动注入Mock不是更容易吗?

对我来说,使用特定绑定进行测试项目对其他类型的测试(集成,功能性)更有用,但即使在这种情况下,您仍然需要根据测试更改绑定。

我的方法是kronhrbaugh和Hamish Smith的混合,creatin是一种“依赖解析器”,您可以在其中注册和取消注册要使用的模块。

+0

我其实同意你的意见。在大多数我自己的单元测试中,我正在做“手动”注入 - 这听起来太简单了,仅仅将模拟传递给构造函数!我在一些单独的特定情况下使用了单元测试中的DI,但是肯定是少数情况。 – 2009-09-10 12:23:57

0

对于我正在开发的项目,我为每个环境(测试,开发,舞台,制作等)创建了单独的模块。这些模块定义绑定。

因为dev,stage和production都使用了很多相同的绑定,所以我创建了一个通用的模块,它们都是从它们派生而来的。每个人然后添加它环境特定的绑定。

我也有一个KernelFactory,当交给一个环境令牌时,它会用适当的模块来缓冲一个IKernel。

这使我可以切换我的环境令牌,它将自动更改我的所有绑定。

但是,如果这是单元测试,我同意上面的意见,一个简单的构造函数允许手动绑定是一种方式,因为它可以让Ninject保持您的测试。

3

我只希望像这样的作品

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

其中ProductionModule是我的生产绑定和我通过在特定的测试案例调用重新绑定覆盖。我把重新绑定的几件物品叫做rebind。

优点:如果有人向生产模块添加新的绑定,我会继承它们,这样它就不会以这种方式破坏,这很好。这一切都在Java中的Guice中工作...希望它也能在这里工作。