2009-10-28 90 views
4

我们是否应该在编写单元测试的时候着眼于DRY,因为功能的变化会影响尽可能少的代码,我们的可预测性,即代码的操作是微不足道的。基本上我问的是在创建非常一般的帮助程序方法和多个单元测试可以使用的帮助程序方法之间的权衡,而不是将测试代码限制在单个单元测试中。作为一个实例采取工厂,具有以下方法签名的情况下:单元测试:干与可预测性

public Node buildNode(String path, String name, Map<String, Object> attributes); 

根据参数提供所得节点对象将是不同的,因此,我们需要测试的不同的可能性。如果我们的目标是可预见性在第一个例子给我们可能会写两个独立的单元测试,但如果我们的目标是干我们宁愿加一个共同的辅助方法,如第二个例子:

EXAMPLE1: 
@Test 
public void testBuildBasicNode() { 
    Node node = testee.buildNode("/home/user", "Node", null); 
    assertEquals("/home/user/Node", node.getAbsolutePath()); 
    assertEquals(false, node.isFolder()); 
} 

@Test 
public void testBuildAdvancedNode() { 
    Map<String, Object> attributes = new HashMap<String, Object>(); 
    attributes.put("type", NodeType.FOLDER); 
    Node node = testee.buildNode("/home/user", "Node", attributes); 
    assertEquals("/home/user/Node", node.getAbsolutePath()); 
    assertEquals(true, node.isFolder()); 
} 

EXAMPLE2: 
@Test 
public void testBuildBasicNode() { 
    Node node = testee.buildNode("/home/user", "Node", null); 
    Node comparisonNode = buildComparisonNode("/home/user", "Node", null); 
    assertEquals(comparisonNode, node); 
} 

@Test 
public void testBuildAdvancedNode() { 
    Map<String, Object> attributes = new HashMap<String, Object>(); 
    attributes.put("type", NodeType.FOLDER); 
    Node node = testee.buildNode("/home/user", "Node", attributes); 
    Node comparisonNode = buildComparisonNode("/home/user", "Node", attributes); 
    assertEquals(comparisonNode, node); 
} 

private Node buildComparisonNode(String path, String name, Map<String, Object> attributes) { 
    // Not just a duplicate of the buildNode method, 
    // can be more trivial if we only limit it to unit tests that share some common attributes 
    ... 
} 

我对第一个例子(可预测性)的问题是,如果任何功能改变(比如说说如何格式化AbsolutePath),它需要在所有单元测试中进行更改。我对第二个例子的问题是,buildComparisonNode感觉像是应该测试的东西,我当然不想开始编写测试测试。

另外,作为一个结束思想,您是否会声明在示例单元测试中使用的字面值字符串的最终变量,还是它们很好?

+0

一个单位案例=一个测试。我没有看到DRY =) – mauris 2009-10-28 06:05:17

回答

8
  1. 好问题。我之前听说过“单元测试可能潮湿,但不能湿透...”。为了保持测试的可维护性,重点(或可预测性)是关键。你的团队越大,我认为你应该对此越重要。另一件需要考虑的事情是,如果有特定的测试助手代码,它本身可以成为一个API,所以如果每个人都知道如何使用它,这可能不是一个坏主意。我的经验法则是,如果我可以在自动重构中使用IDE来完成重复操作,那么我将删除重复项,并且可以给它一个好名字。

  2. 一个建议...看看Nat Pryce的Test Data Builder模式写作关于更可维护/可扩展的方法。

+0

谢谢,那个测试数据生成器模式很巧妙! – Zecrates 2009-10-28 08:28:59

+0

+1:不要着迷于DRY单元测试,这会破坏目的。测试必须超级清晰(值得信赖),而且不一定要高效。任何体面的单元测试库都允许继承,所以你可以很容易地将一些通用代码放在超类中。 – 2009-10-28 15:39:48

4

尽管DRY适用于生产代码,但它并不总是适用于单元测试。你真的希望每个测试都相互独立,这通常意味着重复自己。另一方面,我发现将某些事情分组到所有测试使用的助手方法中是很有用的,只要它不将测试耦合在一起,那么它应该没问题。我通常减少重复的一个地方是使用测试数据构建器来构建我测试中存在于特定状态中的对象。

我的经验法则是保持我的测试尽可能小而且可读。如果使用DRY可以帮助实现,那么我使用它。如果不是,那么我不会。 :-)

希望有所帮助。我不是单元测试方面的世界专家,所以我可能会很错。 :-)

0

另一件事想 - 通常,当你在测试中删除重复它告诉你,什么是产品代码或设计正在等待一个合法的理由(在生产代码库使用)将其移动到生产代码。我记不起我从哪里得到那个啊......但是我认为这与TDD as if you mean it有关。