【问题标题】:How do I isolate a singleton in my unit tests?如何在单元测试中隔离单例?
【发布时间】:2011-08-28 18:38:05
【问题描述】:

我的程序中有一个静态类。在静态构造函数中,我创建了服务实例。服务的代理是单例。

我必须为这个类编写单元测试,当然我想隔离这个服务。 我该怎么做?

在我的项目中,我们使用 Rhino.Mocks。

【问题讨论】:

  • 已显着编辑。如果我搞砸了,请随意回滚或进行其他修复:)
  • 只是...不要写单例。如果你想测试你的代码,第一步必须是实际编写可测试的代码。单身人士不是,这只是one of their many problems
  • 我同意其他人的观点,即依赖注入是最好的方法。如果这对现有代码的重构过于重要,那么您还可以为您的单例添加 public static 设置器,这样至少您可以替换实例。确保给这些设置器起一个名字,确保它们只会在单元测试中被调用(例如public static void SetInstanceForUnitTests(IMyService service)
  • 我无法通过存根为更改服务添加属性。我在静态承包商中创建服务实例。

标签: c# unit-testing rhino-mocks nunit-2.5


【解决方案1】:

从单元测试的角度来看,单例是有问题的。我假设使用您的 Singleton 的类正在通过静态属性检索实例:

class MyClass()
{
   public MyClass()
   {
      var myService = StaticFoo.MyServiceInstance;
   }
}

这相当于在本地创建一个具体的类实例,因为“如何到达”你的 Singleton 的知识是你的类实现的一部分。您需要做的是删除该知识并注入此依赖项,就像您在非单例上注入其他依赖项一样。最直接的方法是构造函数注入:

class MyClass()
{
   public MyClass(IService myService)
   {
     //..
   }
}

另一个需要的部分是确保您的服务中的所有接口方法/属性都定义为虚拟或定义您的服务实现的接口或抽象基类,并定义您的类MyClass 在此服务上使用的所有操作.这是必需的,因为大多数单元测试框架(包括 Rhino-Mocks)只能模拟虚拟方法/属性。

我见过但个人不太喜欢的另一种方法是在您的 Singleton 中引入一个 setter,这样您就可以用单元测试所需的模拟对象“交换”具体类。此设置器仅用于单元测试,并且可以标记为内部,因此您的程序集之外的类无权访问。然后,您可以使用 InternalsVisibleTo 属性为您的单元测试程序集创建一个特殊例外,以便它可以查看和使用 setter。

这种方法的主要优点是它需要更少的重构来获得可测试的解决方案,但代价是“污染”静态 Singleton 持有者的接口。因为在大多数情况下,首先不需要单例,所以我会考虑首先重构为构造函数注入。

【讨论】:

    【解决方案2】:

    您的类需要有一个公共初始化方法,该方法接受服务实例。然后,您可以使用初始化方法注入一个模拟对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多