7

在我的分析器报告中,我越来越多地看到使用依赖注入进行基于模拟的测试的结果。许多依赖项是静态的,但是因为我们想单独测试方法,所以它们被更改为实例成员,如下例所示:

class ShortLivedThing {
   IDependency1 dep1;
   IDependency1 dep2;
   IDependency1 dep3;
   ...

   int TheRealData;

   // Constructor used in production 
   public ShortLivedThing() {
     dep1 = new Dep1(); dep2 = new Dep2(); dep3 = new Dep3();
   }

   // DI for testing 
   public ShortLivedThing(IDependency1 d1, IDependency2 d2, IDependency3 d3) { 
     dep1 = d1(); dep2 = d2(); dep3 = d3();
   }
}

反过来,大多数时候依赖项还有其他依赖项等等。这导致每次在测试之外进行方法调用时都会实例化(主要是“静态”)对象树。每个对象都非常小(只有几个指针),但树效应将其转化为不断增加的性能影响。

我们对于它可以做些什么呢?

4

6 回答 6

8

在我看来,您需要利用适当的依赖注入框架可以为您提供的功能。不要使用不同的构造逻辑进行测试/生产。

使用 spring,单例注入仅在容器启动时执行。每次都进行原型注射。每次运行单元测试时,也会完成完整的接线,如果它正在接线的话。所以分析单元测试通常不是一个好主意。

也许您使用的单例范围太少而原型范围太多?(原型 = 每次都有新实例)

spring 注入的好处是您可以使用范围代理,这意味着您的对象图可以如下所示:

 A Singleton
 |
 B Singleton
 |
 C Prototype (per-invocation)
 |
 D Singleton
 |
 E Session scope (web app)
 |
 F Singleton

并且每个请求只会为每个会话创建 1 个 C 实例和一个 E 实例。A、B、D 和 F 是单例。如果它不是 web 应用程序,则默认情况下您没有会话范围,但您也可以制作自定义范围(“窗口”范围对于窗口桌面应用程序来说听起来很酷)。这里的线索是您可以在任何级别“引入”范围,有效地您可以拥有十层单例对象,并且突然之间会出现一些会话范围。(这确实可以彻底改变您在分层架构中实现某些横切功能的方式,但这是另一回事)

我认为,这确实可以在 DI 模型中尽可能少地创建对象。

尽管这是 Java 的 Spring,但我相信许多其他 DI 框架应该支持类似的特性。也许不是最简约的。

于 2009-01-07T15:38:54.867 回答
2

我认为你应该只有“DI构造函数”。您调用此构造函数进行测试和生产。

class ShortLivedThing {
   IDependency1 dep1;
   IDependency1 dep2;
   IDependency1 dep3;
   ...

   int TheRealData;

   public ShortLivedThing(IDependency1 d1, IDependency2 d2, IDependency3 d3) { 
     dep1 = d1; dep2 = d2; dep3 = d3;
   }
}

这样,每次在测试之外进行方法调用时,您就不会遇到实例化对象树的问题。当然,对于生产,您必须在参与对象本身之外正确连接您的对象,这是一件好事。

总而言之:不要追求 50% DI / 50% 硬编码,而要追求 100% DI。

于 2009-01-07T15:55:24.880 回答
1

传递引用怎么样?

于 2009-01-07T15:44:05.387 回答
1

如果您关心的是缓慢的测试,请尝试并行运行它们,并且不要让测试过程中断您的程序员。

自动化这个过程:

  • 当有人签入时,在存储库之外进行构建。
  • 在此构建上运行测试。
  • E - 将结果邮寄给签到的开发人员。

最好不要对实际存储库进行首次签入。把它变成一个临时的,并以此为基础进行构建。您可以选择执行性能测试、样式检查等,并将这些内容包含在电子邮件中。如果您这样做,请在自动化流程中添加一个步骤:

  • 如果测试通过(并且满足可选条件),则将新代码与实际存储库合并。

通过这种方式,缓慢的测试就不用担心了。此外,当开发人员需要知道她的代码是否破坏了某些东西或实现了预期的性能提升时,她只需签入并等待为她生成的电子邮件。

于 2009-01-07T15:54:15.193 回答
0

我能想到的最好的办法是将所有依赖项放入一个“上下文”对象中,然后在所有实例之间共享。这应该会在一定程度上缓解性能问题。

于 2009-01-07T15:54:10.730 回答
0

如果您的目标是 .NET,请查看Autofac。它具有各种范围(单例、工厂、容器)来调整创建方面,确定性处置以控制资源使用,并允许使用 GeneratedFactories 和 lambda 表达式来配​​置组件并避免反射成本。

于 2009-01-07T18:00:57.200 回答