2011-05-23 40 views
3

我有一个“配方”方法,我试图用TDD编写。它基本上召唤出不同的方法,有时使得基于这些方法的结果决定:单元测试理念

public void HandleNewData(Data data) 
    { 
    var existingDataStore = dataProvider.Find(data.ID); 
    if (data == null) 
     return; 

    UpdateDataStore(existingDataStore, data, CurrentDateTime); 

    NotifyReceivedData(data); 

    if (!dataValidator.Validate(data)) 
     return; 

    //... more operations similar to above 
    } 

我的本能反应是,我确认HandleNewData调用上面传递的预期参数见过的方法来启动编写测试用例并且在方法调用失败的情况下返回。 但是这种感觉对我来说就像是一次巨大的投资,用来编写这样一个测试,几乎没有实际的好处。

那么写这样一个测试的真正好处是什么?还是真的不值得这么麻烦?

它似乎只是代码本身的一个超规范,并且只要代码需要调用另一个方法或决定不再调用当前方法之一,就会导致维护问题。

回答

9

TDD并不意味着为已经存在的代码编写单元测试(尽管有时在改进遗留代码时可能有必要)。

你可能听说过“红色,绿色,重构”这个词。这是我们在进行TDD时采取的方法。以下是这三个法律测试驱动开发的,它采取远一点......

  1. ,直到你写一个失败的 单元测试你可能不写产品代码 。
  2. 您不可以编写更多的单元测试,而不足以失败,并且 不编译失败。
  3. 您不能写出更多的产品代码,而不足以通过当前失败测试的 。

采用这种方法的好处是,您的单元测试覆盖率非常接近100%,并且您知道您的代码完全按照指定的方式工作。

它会减少维护问题,因为只要有人对您的代码进行更改并运行测试,他们就会知道它们是否破坏了任何东西。

在这种情况下,我会在添加任何HandleNewData()之前,为从HandleNewData()调用的方法递增地添加单元测试。

向遗留代码添加单元测试非常困难,但是可行且非常值得。如果你还没有,我真的推荐阅读Working Effectively with Legacy CodeMichael Feathers。在为25年的代码库添加单元测试时,我发现它非常宝贵。

+1

我应该澄清,我正在做的是重构一些旧的遗留代码。我的愿望是让它看起来像上面这个简单的例子,我试图用TDD来实现上面的实现 – dmg 2011-05-23 22:46:51

+1

我认为Johnsyweb的观点是测试驱动设计首先不会达到上述实现,你有很好的理由难以测试,这是一个警告信号,表明这是一个糟糕的设计。 你当然可以在测试中包装这个实现,但是如果你的测试没有绑定到当前的实现上,你也可能想要考虑你的测试会是什么样子。如果你要开始一个高层次并深入研究行为,这个方法将实现你将要写什么测试,什么组件,你能在这里使用这个结构吗? – Jonah 2011-05-24 00:10:20

3

您遇到的问题非常普遍。你有一些令人讨厌的未经测试的遗留代码太多了,并且与太多的合作者密切相关。为此写一篇测试确实很痛苦。

问题是,你不幸背负着这个代码债务,并在某些时候,你将不得不支付。

因此,要开始支付一部分债务,如果您需要更改此代码,我会尽可能地模拟出测试方法的单个传递,以便您可以获得测试shell适合您添加新功能的地方。如果可能的话,我会把你的新功能调用给另一个协作者,这是你可以放置(和测试驱动器!)你的新代码的地方。

通过这种方式,您可以确信旧代码会调用您的新代码,并且新代码已通过TDD正确构建。

您当然还有原始遗留代码的代码债务,但您可以将其作为单独问题来处理。

1

由于您已经澄清它是“Legacy Code(TM)”,因此我将简单介绍该方法的设计。名字本身是模糊的,这反映在方法的内容中。我会看看改进设计 - 它似乎做了很多。

但要做到这一点,我必须确保我不会以“变得更好”为借口而变得更糟。我如何证明这一点?测试!

所以我首先将“副”测试放在顶层对象功能的“块”上。 所以我把在验证“HandleNewData”的行为,今天我所知的测试(这可能包括一些代码开挖)

  • 查找数据存储的ID
  • 更新用新的数据和数据存储修改时间戳
  • 通知感兴趣的听众
  • 验证数据(它应该是第2步,从它的外观) 等。

一旦我通过一些自动“副”测试确定了现有行为,我现在可以自信地进行更改/改进。也可能是这样的,一旦设计被重构,HandleNewData的包含类型就不再需要了。在这种情况下,您可以放弃这些测试 - 然而,这些测试在现有改进之间的价值不容忽视。