为使用IoC.Resolve()的服务创建单元测试的最佳方法是什么?

时间:2012-07-11 13:51:11

标签: c# .net unit-testing ioc-container

我通过构造函数注入使用IoC容器而不是DI。有些人可能会问我为什么使用IoC容器而不是构造函数注入。嗯,原因如下:Why do I need an IoC container as opposed to straightforward DI code?。 但是,我发现很难为我的服务创建单元测试。我不确定如何在运行时模拟我的服务使用的存储库,因为我没有使用构造函数注入(一个难题)。有人有任何解决方案?

例如:

public class SomeService
{
    private ISomeServiceRepository someServiceRepository;

    public GetSomeThing()
    {
        //how do I mock this repository in my unit test
        someServiceRepository = IoC.Resolve<ISomeServiceRepository>();

        someData = someServiceRepository.getData();

        someOtherService = new SomeOtherService();
        someThing = someOtherService.GetSomeThing();

        return FigureOutSomeThingElse(someData, someThing);
    }

    public FigureOutSomeThingElse(someData, someThing)
    {
        //do some figuring
        return somethingElse;
    }
} 

public class SomeOtherService
{
    private ISomeServiceRepository someOtherServiceRepository;

    public GetSomeThing()
    {
        //how do I mock this repository in my unit test
        someOtherServiceRepository = IoC.Resolve<ISomeOtherServiceRepository>();

        var someData = someOtherServiceRepository.getData();
        return someData;
    }
}

3 个答案:

答案 0 :(得分:2)

人们可以根据需要轻松地解决关于“你做错了”的问题,但是我已经经历了旧代码库的痛苦,这种代码库无法无法理解寄存器解析 - 释放模式。但是,我建议不要使用静态容器引用(如在IoC.中)并尝试使容器本身至少注入。

在这种情况下,您只需提供IoC注册容器的测试版本,但不是通常的应用程序配置,而是编写测试配置。

这不会是真正意义上的嘲弄或抄袭,实际上这些术语可能会使解决方案相当混淆。

实质上,在单元测试中它只是一个不同的活动配置。您仍然需要手动注册类型,如果它位于单元测试的执行路径中,您可能无法禁用当前配置代码。

我们有一个容器可以根据需要在IoC模式中解决(对于少数几种类型),并且为了测试我们只是创建了一个新容器(基本上,一个用于代码的配置和另一个用于单元测试的配置)。我们的用例略有不同,因为我们将容器注入类而不是像您的情况那样具有静态可访问类型。

答案 1 :(得分:2)

你真的不应该在你的类方法中使用IOC.Resolve。无论如何,它非常接近new你的代码,这是你试图避免使用DI的一部分。写一个更好,更可测试的方法就是这样。

Public Class SomeService
{

  ISomeServiceRepository someServiceRepository;

  public SomeService(ISomeServiceRepository someServiceRepository)
  {
    this.someServiceRepository = someServiceRepository
  }

  Public GetSomeThing()
  {

    someData = someServiceRepository.getData();
    ...
  }
}

通过这种方式,您可以模拟您的界面并直接注入它。

如果你必须使用.Resolve,那么亚当的方法是你能做的最好的方法

答案 2 :(得分:0)

我明白了,这就是我做的......

(我正在使用Unity.Net和RhinoMock)

  • 创建了一个实现IContainerAccessor的baseUnitTest类

  • 在构造函数中创建并初始化UnityContainer的新实例

  • 创建了一个“UnityContainer”readonly公共属性,它实现了IContainerAccessor.Container

  • 创建了一个继承baseUnitTest的单元测试类

  • 在测试方法中添加了以下代码......

    Mocker = New Rhino.Mocks.MockRepository()

    SomeRepository = Mocker.DynamicMock(OfomeomeRepository)()

    MyBase.UnityContainer.RegisterInstance(OfomeomeRepository)(SomeRepository)

    Rhino.Mocks.Expect.Call(Of SomeResult)(SomeRepository.SomeMethod(“someArg”))。Return(New SomeResult())

    Mocker.ReplayAll()

    someService = New SomeService()

    '此方法使用IOC.Resolve(ISomeRepository)并调用SomeRepository.SomeMethod someResult = someService.someMethod()

    Assert.IsNotNull(someResult)

  • 完成