5

我只是想知道在内核中重新连接绑定的最佳实践是什么。

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

对于测试,我想覆盖这些绑定,以便可以交换我的 Test Doubles / Mocks 对象。

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

覆盖 IDepend 的任何现有绑定?

4

6 回答 6

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

使用这样的模式,您可以IDependencyResolver通过调用RegisterResolver模拟或假实现来设置 from 单元测试,该实现返回您想要的任何对象,而无需连接完整的模块。如果您将来选择切换到不同的 IoC 容器,它还具有从特定 IoC 容器中抽象代码的第二个好处。

当然,您还想根据需要添加其他方法IDependencyResolver,我只是在此处包括基础知识作为示例。是的,这将要求您围绕 Ninject 内核编写一个超级简单的包装器,该内核也实现IDependencyResolver了。

您想要这样做的原因是您的单元测试实际上应该只测试一件事,并且通过使用您的实际 IoC 容器,您实际上锻炼的不仅仅是一个被测类,这可能会导致您的测试出现误报脆弱并且(更重要的是)随着时间的推移动摇开发人员对其准确性的信心。这可能导致测试冷漠和放弃,因为测试可能会失败,但软件仍然可以正常工作(“别担心,总是失败,这没什么大不了的”)。

于 2009-08-12T03:42:01.947 回答
3

我只是希望这样的事情有效

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

其中 ProductionModule 是我的生产绑定,我通过在特定测试用例中调用 Rebind 来覆盖。我对我重新绑定的几个项目调用重新绑定。

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

于 2011-12-28T17:54:47.823 回答
1

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

我意识到您的项目/解决方案设置可能不允许这种组织,但从我所看到的情况来看,它似乎很典型。

于 2009-08-12T02:53:49.057 回答
1

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

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

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

于 2009-08-17T10:27:54.110 回答
0

对于我正在进行的项目,我为每个环境(测试、开发、阶段、生产等)创建了单独的模块。这些模块定义了绑定。

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

我也有一个 KernelFactory,当传递一个环境令牌时,它会使用适当的模块来处理一个 IKernel。

这允许我切换我的环境令牌,这反过来会自动更改我的所有绑定。

但如果这是用于单元测试,我同意上面的评论,即允许手动绑定的简单构造函数是可行的方法,因为它使 Ninject 远离您的测试。

于 2009-08-28T05:23:48.043 回答
0

我会向 MyClass 添加一个接受模块的构造函数。
这不会在生产中使用,但会在测试中使用。
在测试代​​码中,我将传递一个定义所需测试替身的模块。

于 2009-08-12T02:46:13.203 回答