是否几乎可以在不使用java中的任何DI框架的情况下做好TDD(或BDD)?

时间:2010-09-16 12:19:32

标签: java unit-testing dependency-injection tdd bdd

IMO良好TDD的一个主要特征是:单独测试你的类(或实际单元)。

当您这样做时,您可以在每个测试中实际测试单个行为 - 对于您遇到的单个问题,只会有一个测试。

为此,您首先必须验证您的类中没有静态引用(包括构造函数AKA new关键字)。

理论上很容易不使用任何依赖注入框架并在完全隔离中测试您的类,为此您需要在构造函数中注入所有依赖项并创建将调用new关键字的Factories类。 / p>

我发现这个理论上的MO在实践中实际上太难了。

我在这个过程中遗漏了哪些重要内容?

修改: 我认为我们都应该针对一次代码更改失败。它永远不会是完美的,但它会使你的代码更接近那里,人们忘记测试代码也是可维护的代码,测试代码更改的规范

如果你不瞄准那里,你最终会删除那些不好的解决方案

3 个答案:

答案 0 :(得分:10)

我认为您不需要DI框架进行单元测试 - 在DI成为时尚之前,TDD已经存在了很多年。如果代码本身不使用显式DI框架,为什么要将它用于单元测试呢?

当然,DI框架可以在一定程度上简化任务。 OTOH他们通过将复杂性放在其他地方来实现。我的偏好是进行自包含单元测试,到目前为止,我几乎总是在没有DI框架的情况下进行管理。这通常需要重构我的测试代码,有时甚至需要复制代码,但我可以忍受。而且,正如@kostja所说,模拟框架也是一个很大的帮助。

设计可测试性也很重要 - 如果一个类本身很难测试,最好重构它而不是试图通过DI框架来减轻痛苦。

请注意,所有这些都需要大量的练习,以及思维方式和思维方式的改变。所有这些都需要时间和耐心......你跟上的越多,你就越好: - )

  

您可以在每个测试中实际测试单个行为 - 对于您遇到的单个问题,只会进行一次测试。

我认为你的结论在这里是错误的。即使您在每个测试中测试一件事,您也可以通过单个代码更改来打破大量测试。 更新:每项测试都是验证测试代码中的单个执行路径。但是,这些执行路径很少是独立的 - 除了最简单的方法之外,所有路径都是常见的路径部分。任何改变都可能导致多次测试。

答案 1 :(得分:1)

DI应该减少直接依赖性,实际上简化了模拟和单元测试。也许你错过了模拟部分?这个post可以澄清事情。

答案 2 :(得分:0)

当然, 是可能的。 很容易。例如,假设您要测试以下方法,该方法在Database类(静态持久性外观)上调用静态方法并直接实例化SimpleEmail(来自Apache Commons Email):

public void doBusinessOperationXyz(EntityX data) throws EmailException
{
   List<EntityX> items = 
      Database.find("select x from EntityX x where x.xyz=?", data.getXyz());

   BigDecimal total = ...compute/obtain the value...
   data.setTotal(total);

   Database.persist(data);

   Email email = new SimpleEmail();
   email.setSubject("Notification about processing of ...");
   email.addTo(data.getCustomerEmail());
   email.setMsg("...some notification message...");
   email.send();
}

使用JMockit进行以下单元测试,可以独立于DatabaseEmail依赖关系测试此方法:

@Test
public void doBusinessOperationXyz() throws Exception
{
   final EntityX data = new EntityX(5, "abc", "5453-1");
   final List<EntityX> items = new ArrayList();
   items.add(new EntityX(1, "AX5", "someone@somewhere.com"));

   new Expectations()
   {
      @Mocked final Database unused = null;
      @NonStrict SimpleEmail email;

      {
         Database.find(withSubstring("select"), (Object[]) any); result = items;
         Database.persist(data);

         email.send(); times = 1;
      }
   };

   new MyBusinessService().doBusinessOperationXyz(data);
}

通常,有必要为DatabaseEmail依赖项注入模拟对象,从而强制使用更复杂的解决方案。通过模拟/存根静态方法和构造函数,我们可以保持简单,自然,优雅的设计。