【问题标题】:What's the best way to create unit tests for services that uses IoC.Resolve()为使用 IoC.Resolve() 的服务创建单元测试的最佳方法是什么
【发布时间】:2012-07-11 13:51:11
【问题描述】:

我通过构造函数注入使用 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;
    }
}

【问题讨论】:

  • “有人有解决方案吗?”。是:使用构造函数注入!
  • 构造函数注入会很快变得一团糟,除非您正在构建夫妻应用程序。例如,我从这个 New SomeService (ISomeRepository) 开始,但后来随着需求的变化,我必须添加这个 New SomeService(ISomeRepository, IAnotherRepository)。现在我得回去改一堆代码了。
  • 如果 ctor 注入变得混乱,那么你做错了。在 SO 上搜索,您会发现很多关于 ctor 注入的信息,以及许多很好的答案,解释了为什么 ctor 注入是可行的方法,以及当它不适合您时您做错了什么。
  • 嘿,我思想开放,但是您如何解决我上面解释的问题?
  • “出于此处所述的原因”。你能重申一下你的确切问题是什么。我很难从提供的链接中找出您的问题。

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


【解决方案1】:

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

Public Class SomeService
{

  ISomeServiceRepository someServiceRepository;

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

  Public GetSomeThing()
  {

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

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

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

【讨论】:

  • 它不在newing 附近,提供的内容由外部决定,因此 IoC 中的 I。从静态容器解析的一个缺点是对服务定位器模式的依赖。我们的解决方案在中间遇到了,我们注入容器所以有一种 DIoC 哈哈
  • 贾斯汀,感谢您的意见,但您需要看看这个 -> stackoverflow.com/questions/871405/…。这就解释了为什么你不想使用构造函数注入。
  • @JustinPihony 就是说,我同意你的观点,即在这种情况下,注入很容易测试并且可能更可取。
  • @JoeWeb 和 Adam 我会用blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx 反驳,而且,看了一眼(我稍后会更彻底地阅读),我不同意 IoC 的使用。你应该有一个顶级的.Resolve,当然,但我说它不应该在你的孩子方法中。我会更彻底地阅读该帖子并在那里留下更全面的回应
  • @JustinPihony 我是反模式论点的守望者,因为我从未遇到过可行的替代方案。也就是说,如果要注入 IoC 容器,您可以减轻服务定位器行为的一些负面影响。许多现有的应用程序(例如,从一开始就不使用 IoC 的应用程序)只是无法了解 register-resolve-release 模式,不幸的是我亲身体验过 - 因此我们发现了平衡。它被证明是可测试和可维护的,这减少了我对反对它的狂热论点的关心。
【解决方案2】:

人们可以根据需要轻松发起关于“你做错了”的批评,但我经历过旧代码库无法无私地理解 register-resolve-release 模式的痛苦。但是,我建议不要使用静态容器引用(如IoC.)并尝试使容器本身至少被注入。

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

这并不是真正意义上的嘲弄或存根,事实上这些术语可能会使解决方案相当混乱。

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

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

【讨论】:

    【解决方案3】:

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

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

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

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

    • 创建了一个实现 IContainerAccessor.Container 的“UnityContainer”只读公共属性

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

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

      Mocker = 新 Rhino.Mocks.MockRepository()

      SomeRepository = Mocker.DynamicMock(Of ISomeRepository)()

      MyBase.UnityContainer.RegisterInstance(Of ISomeRepository)(SomeRepository)

      Rhino.Mocks.Expect.Call(Of SomeResult)(SomeRepository.SomeMethod("someArg")).Return(New SomeResult())

      Mocker.ReplayAll()

      someService = 新的 SomeService()

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

      Assert.IsNotNull(someResult)

    • 完成

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-11
      • 2018-02-05
      • 2019-02-12
      • 1970-01-01
      • 2017-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多