IMO良好TDD的一个主要特征是:单独测试你的类(或实际单元)。
当您这样做时,您可以在每个测试中实际测试单个行为 - 对于您遇到的单个问题,只会有一个测试。
为此,您首先必须验证您的类中没有静态引用(包括构造函数AKA new
关键字)。
理论上很容易不使用任何依赖注入框架并在完全隔离中测试您的类,为此您需要在构造函数中注入所有依赖项并创建将调用new
关键字的Factories类。 / p>
我发现这个理论上的MO在实践中实际上太难了。
我在这个过程中遗漏了哪些重要内容?
修改: 我认为我们都应该针对一次代码更改失败。它永远不会是完美的,但它会使你的代码更接近那里,人们忘记测试代码也是可维护的代码,测试代码更改的规范。
如果你不瞄准那里,你最终会删除那些不好的解决方案
答案 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进行以下单元测试,可以独立于Database
和Email
依赖关系测试此方法:
@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);
}
通常,有必要为Database
和Email
依赖项注入模拟对象,从而强制使用更复杂的解决方案。通过模拟/存根静态方法和构造函数,我们可以保持简单,自然,优雅的设计。