我刚刚从亚马逊购买了The Art of Unit Testing。我非常认真地了解TDD,所以请放心,这是一个真正的问题。
但我觉得我一直在寻找放弃它的理由。
我将在这里扮演魔鬼的拥护者并试图击败TDD的所谓好处,希望有人可以证明我是错的,并帮助我对其美德更有信心。我想我错过了一些东西,但我无法弄清楚是什么。
1。 TDD减少错误
这个often-cited blog post表示单元测试是设计工具而不是用于捕获错误:
根据我的经验,单元测试不是 找到错误或检测回归的有效方法。
...
TDD是一种强大的设计方式 软件组件(“单位”) 交互式地使他们的行为 通过单元测试指定。 这就是全部!
有道理。边缘情况仍然总是在那里,而你只会找到表面上的错误 - 无论如何,只要你运行你的应用程序就会找到它们。在构建了大量软件之后,您仍需要进行适当的集成测试。
公平地说,减少错误并不是TDD应该提供的唯一帮助。
2。 TDD作为设计范例
这可能是最重要的一个。 TDD是一种设计范例,可以帮助您(或强迫您)使您的代码更加composable。
但可组合性是多重可实现的质量;例如,函数式编程风格使代码也可以组合。当然,完全以功能风格编写大型应用程序很困难,但是为了保持可组合性,您可以遵循某些折衷模式。
如果您从高度模块化的功能设计开始,然后根据需要小心地将状态和IO添加到您的代码中,您将得到TDD鼓励的相同模式。
例如,为了在数据库上执行业务逻辑,IO代码可以在一个函数中被隔离,该函数执行访问数据库的“monadic”任务并将其作为参数传递给负责业务逻辑的函数。这将是实现它的功能性方法。
当然,这有点笨重,所以相反,我们可以将数据库IO代码的一个子集放入一个类中,并将其提供给包含相关业务逻辑的对象。这是完全相同的事情,是功能性做事方式的改编,它被称为存储库模式。
我知道这可能会给我带来非常糟糕的鞭挞,但有时候,我不禁觉得TDD只是弥补了OOP可以鼓励的一些坏习惯 - 可以避免的从功能风格中汲取灵感。
第3。 TDD作为文档
据说TDD可以作为文档,但它只作为同行的文档;消费者仍然需要文本文档。当然,TDD方法可以作为示例代码的基础,但是测试通常包含一定程度的模拟,这些模拟不应该在示例代码中,并且通常是非常人为的,因此可以对它们进行相等的评估。预期的结果。
一个好的单元测试将在其方法签名中描述正在验证的确切行为,并且测试将不再验证该行为。
所以,我会说,你的时间可能会更好地用于完善你的文档。哎呀,为什么不先彻底完成文档,并称之为文档驱动设计?
4。用于回归测试的TDD
上面的帖子中提到,TDD对于检测回归并不太有用。当然,这是因为当您更改某些代码时,非显而易见的边缘情况总会陷入困境。
在该主题上可能还需要注意的是,很长一段时间大多数代码将保持相同的可能性很大。那么,每当代码改变时,根据需要编写单元测试,保留旧代码并将其结果与新函数进行比较,是不是更有意义?
答案 0 :(得分:5)
在设计方面,你没有看到TDD的一个主要优点是它驱动设计就足够了。你知道他们说工程师看到的玻璃是它应该的两倍大。软件中的过度设计可能是一个大问题。我发现90%以上的时间TDD强制推出正确的抽象平衡,以支持以后扩展代码。 TDD并不神奇,它背后的程序员也可以做到这一点,但它是工具包的重要组成部分。
我认为列表中的TDD太多了。重构怎么样?我认为测试的一个主要优点是它可以锁定行为,因此当你重构时,你可以确信你没有改变任何东西,这反过来可以让你对重构充满信心。没有什么比设计出生的经验而不是白板(尽管高级白板设计仍然非常重要)。
另外,你写道:“那么,每当代码改变时,根据需要编写单元测试,保留旧代码并将其结果与新函数进行比较,是不是更有意义?”在没有单元测试的情况下编写的代码通常是不可测试的,特别是如果它与外部服务(如数据库,事务管理器,GUI工具包或Web服务)交互时。稍后添加它们就不会发生。
我认为Bob Martin说得最好,TDD就是编写Double Entry对Accounting的编程。它可以防止某类错误。它不会阻止所有问题,但确实如果你的程序想要添加两加二,它就不会减去它们。基本行为很重要,当它出错时,您可以花很多时间来了解调试器。
答案 1 :(得分:3)
我相信TDD的好处在于你实际上编写了测试,因为当它们是你必须实现的目标时更有趣,(创建通过测试的代码),而不是之后你必须做的苦差事。
此外,它让您了解用户的想法。你必须考虑“那么用户需要我的方法做什么”或者其他什么,而不是“我希望我的方法已经实现了它应该做的”。通过这种方式,它也可以帮助减少错误。
答案 2 :(得分:2)
TDD不是一种方法论,它是一种心态。
TDD减少错误:随着您的代码库开始增长,在每个签入到源代码管理上运行所有测试非常重要。当您在团队中有新成员时,这一点的好处就变得很明显。
TDD作为设计范例:测试是代码的第一批用户。最初,使用测试来驱动您的设计非常困难。但是,一旦你对它感到满意,你就会意识到测试代码实际上可以帮助你决定你的设计。这自然具有一些TDD经验。例如,使用TDD,您希望在界面中包装对服务的访问。这允许你模拟。但是,最重要的是它是正确的设计方式。
TDD作为文档该文档适用于代码开发人员使用的代码文档。开发人员发现很容易阅读编写良好的单元测试,而不是文档的页面和页面。
答案 3 :(得分:1)
我只想强调:
TDD测试不是单元测试
“单元测试的目的是单独测试一个代码单元。”
“与单元测试不同,TDD测试可能会一次测试多个代码单元。” TDD测试是更多的交互测试....
来自Stephen Walter非常好的博文TDD Tests are not Unit Tests
答案 4 :(得分:1)
在Javascript或任何必须在浏览器等多个古怪环境中运行的语言中,单元测试是告诉 Internet Effing Explorer搞砸了修复内容的好方法。
至少它比锤击F5并使用console.log()
甚至警报更好。
但是,我认为这应该主要用于中间件或复杂的RIA - 因为TDD用户界面几乎不可能具有很少的业务逻辑。
如果你写下一个jQuery,单元测试将是你的朋友! :)
哦,让我们不要忘记像JSTestDriver这样的js的好工具!
答案 5 :(得分:0)
TDD还为您提供了自动化单元测试完全涵盖的代码。这只是因为在完成失败的单元测试通过之前,不会编写任何代码。