2013-07-16 47 views
3

这是我们反复讨论的内容,人们的观点在这方面似乎差异很大。在TDD重构之后编写更多的单元测试

基本问题,在做TDD时,应该在循环的重构步骤之后添加额外的单元测试。我不是在谈论你的下一个测试来开始你的下一个周期,而是测试来覆盖由于重构而产生的任何变化。

这可以用一个真实的例子来解释。一个TDD周期,我们有以下的代码的绿色后:

public bool ShouldVerifyDomain 
    { 
     get 
     { 
      return this.Orders.SelectMany(x => x.Items).Any(x => x.Product.RequiresDomainVerification); 
     } 
    } 

现在,我看这一点,并认为,hmmmm,那LINQ说法可能有点整洁,有点更容易阅读,不违反迪米特相当这么多,让我们重构一下。所以我创建Order如下:

public bool HasItemsThatRequireDomainVerification 
{ 
    get 
    { 
      return this.Items.Any(x => x.Product.RequiresCascadeDomainVerification); 
    } 
} 

和改性ShouldVerifyDomain到:

public bool ShouldVerifyDomain 
    { 
     get 
     { 
      return this.Orders.Any(x => x.HasItemsThatRequireDomainVerification); 
     } 
    } 

OK,那是找一个好一点,我与快乐。让我们继续对列表中的下一个测试......但是......等等,我们现在通过另一个对象上的属性测试属性HasItemsThatRequireDomainVerification ....是一个真正的单元测试还是应该添加一个测试( s)直接测试HasItemsThatRequireDomainVerification

我感觉如何?我不认为这会增加很多价值。我认为这会增加套件的维护负担,需要时间,并且在进行未来更改时不会给我们更多的信心。

它可能给我们什么?公开界面Order的“文档”。

想法?

回答

8

您的重构步骤添加或更改了功能吗?如果是这样,那么这是一个无效的重构步骤。您应该退出这些更改并首先为新功能添加测试。

但是,在你的例子中,我不认为必然如此。你所做的一切与提取方法非常相似。您将现有逻辑合并到另一个位置,并从现有位置调用该逻辑。现有的测试仍在测试。

重构后,如果你担心需要添加更多的测试,那么你应该看的第一个地方就是测试覆盖率。如果你仍然是100%(或者与重构之前的接近),那么你可能仍然很好。如果,另一方面,重构添加未被覆盖的测试代码路径,那么你有一些选择:

  • 请问你的代码需要这些代码路径?如果是这样,测试是不够的。您应该退出重构,为新的代码路径添加失败的测试,然后添加新的代码路径。
  • 如果您的代码不是需要这些代码路径,那么他们为什么在那里?摆脱他们。

你问什么是非常相似的关于测试覆盖一个古老的疑问,一直问有多种形式:

  • 我应该测试私有成员?
  • 我应该为每种方法写一个单独的测试吗?
  • 每个对象的每个成员都应该进行测试吗?

就像所有事情一样,答案总是“取决于”。所有的代码都应该被测试,但是每行代码都不需要自己的测试。例如,假设我有一个类的属性:

public class Customer 
{ 
    public string Name { get; set; } 
} 

我是否需要写一个测试实例化Customer,一个Name值写入到它,然后声称它可以回读同样的价值?显然不是。如果那失败了,那么深深的错了。但是,如果测试覆盖了这一行代码?绝对。某处应该有一个使用CustomerName的测试。如果没有,如果系统中没有测试使用这个属性,那么测试是不完整的,或者这个属性实际上不是系统需要的,应该被删除。

换句话说,当你编写测试时,你并没有真的测试代码。您正在测试系统的功能。实现该功能的代码与测试是分开的并且与测试平行。这两个人不需要了解彼此的大量细节。如果某些东西的外部可见功能发生变化,则测试应该更改为匹配(并验证)它。如果外部可见功能没有改变,则测试不应该改变。他们仍然应该验证相同的功能。

4

当你在进行TDD时,你的测试应该处于“功能测试”功能级别,所以只要功能没有改变,你就不应该改变你的测试。

只要功能的输入和输出相同,更改实现或重构就被认为是TDD中的细节。

TDD不应该让你拥有100%的覆盖率。如果另一方面你使用你的单元测试作为代码解释或者想要100%覆盖(我们在这里说单元测试,因为它们只应该定位一块代码),那么这些单元测试应该改变每一次执行情况以适应所有情况,但这不是TDD的目标。

+0

为什么downvote? – Skyp

2

你没有改变行为,只有语法。代码仍然以相同的方式工作,只是写法不同而已。我认为只要它仍然以相同的方式工作,你的单元测试仍然可靠。

我认为如果重构会要求我们开始测试我们重构的新代码,我们最终会陷入一个兔子洞。什么时候结束?

相关问题