使用IoC时,单元测试的策略应该是什么?

时间:2010-02-21 12:45:38

标签: c# unit-testing dependency-injection inversion-of-control castle-windsor

毕竟我读到的关于依赖注入和IoC的内容我决定尝试在我们的应用程序中使用Windsor Container(它是一个50K LOC多层Web应用程序,所以我希望它不是一个矫枉过正)。我使用了一个简单的静态类来包装容器,并在启动应用程序时对其进行初始化,这对于现在来说非常好。

我的问题是关于单元测试。我知道通过让我可以将类协作者的存根/模拟实现注入到被测试的类中,DI将使我的生活变得更加轻松。我已经使用这种技术编写了几个测试,这似乎对我有意义。我不确定的是我是否应该在单元测试中使用IoC(在本例中为Windsor Castle)(可能以某种方式将其配置为针对我的特殊情况返回存根/模拟)或者是否更好地连接所有依赖项在测试中手动。您如何看待以及哪种做法对您有用?

4 个答案:

答案 0 :(得分:20)

在单元测试中不需要DI容器,因为依赖关系是通过使用Rhino MocksMoq等框架生成的模拟对象提供的。因此,例如,当您测试一个依赖于某个接口的类时,通常通过构造函数注入来提供此依赖项。

public class SomeClassToTest
{
    private readonly ISomeDependentObject _dep;
    public SomeClassToTest(ISomeDependentObject dep)
    {
        _dep = dep;
    }

    public int SomeMethodToTest()
    {
        return _dep.Method1() + _dep.Method2();
    }
}

在你的应用程序中,你将使用DI框架在构造函数中传递ISomeDependentObject的一些实际实现,它本身可以依赖于其他对象,而在单元测试中你创建一个模拟对象,因为你只想测试这个班是孤立的。 Rhino Mocks示例:

[TestMethod]
public void SomeClassToTest_SomeMethodToTest()
{
    // arrange
    var depStub = MockRepository.CreateStub<ISomeDependentObject>();
    var sut = new SomeClassToTest(depStub);
    depStub.Stub(x => x.Method1()).Return(1);
    depStub.Stub(x => x.Method2()).Return(2);

    // act
    var actual = sut.SomeMethodToTest();

    // assert
    Assert.AreEqual(3, actual);
}

答案 1 :(得分:3)

我正在开发一个包含大约400个单元测试的ASP.NET MVC项目。我使用Ninject进行依赖注入,使用MBUnit进行测试。

Ninject对于单元测试并不是必需的,但它减少了我必须键入的代码量。我只需要指定一次(每个项目)我的接口应该如何实例化,而不是每次初始化被测试的类时都这样做。

为了节省编写新测试的时间,我创建了尽可能多的通用设置代码的测试夹具基类。这些类中的设置过程初始化假存储库,为测试用户创建一些测试数据和虚假标识。单元测试仅初始化太具体的数据,无法进入通用设置过程。

我也在某些测试中嘲笑对象(而不是伪造),但我发现伪造数据存储库会导致更少的工作和更准确的测试。

例如,在使用存储库模拟时,检查测试中的方法是否正确地将所有更新提交到存储库会比使用存储库假时更难。

一开始就设置了相当多的工作,但从长远来看,确实帮助我减少了大量的时间。

答案 2 :(得分:2)

我刚刚写了一个非常相似的风格和大小的应用程序。我不会在单元测试中添加任何依赖注入,因为它不够复杂到必要。你应该使用一个模拟框架来创建你的模拟(RhinoMocks / Moq)。

Moq中的Automocking或Rhinomocks中的Auto Mock Container也可以进一步简化您的模拟构建。

  

自动模拟允许您获取对象   您想要测试的类型   手工设置模拟。所有   依赖项会自动模拟   (假设它们是接口)和   注入到类型构造函数中。如果   你需要你可以设置预期   行为,但你没有。

答案 3 :(得分:0)

正如达林已经指出的那样,如果你有嘲笑,你不需要使用DI。 (但是,DI还有一些其他好处,首先包括减少代码中的依赖性,这使得代码在长期运行中更容易维护和扩展。)

我个人更喜欢在单元测试中连接所有内容,因此尽量少依赖外部框架,配置文件等。