测试驱动开发是否将重点放在设计上?

时间:2008-09-17 05:20:05

标签: architecture tdd agile

我对TDD的看法很复杂。虽然我相信测试,但我对测试的想法存在问题,导致我的开发工作。

当您编写代码来满足为接口编写的一些测试时,您可能会将注意力从构建可维护代码,简洁设计和声音架构转移到其中。

我有一个问题,没有测试驱动。有什么想法吗?

10 个答案:

答案 0 :(得分:44)

没有

如果做得好,测试驱动开发是您的设计工具。

我希望您原谅我链接到my own blog entry, wherein I discuss the pitfalls of Test Driven Development that went wrong只是因为开发人员将他们的测试仅仅视为测试。

在之前的项目中,开发人员使用了一种高度破坏性的单例模式,在整个项目中强制执行依赖关系,这在需求发生变化时突破了整个过程:

  当它开始时,TDD被视为一项任务   应该被视为一个   做法。 [...]

     

无法识别   TDD不是关于测试,而是测试   关于设计。猖獗的案例   在单元测试中单独滥用   这显而易见:而不是测试   作家思考“WTF就是这些   singleton = value;做的陈述   我的测试?“,测试作者只是   将单身人士传播到了   试验。 330次。

     

不幸的后果是   构建服务器强制执行的测试是   无论如何都要通过。

正确的测试驱动开发应该让开发人员高度意识到紧耦合,违反DRY(不要重复自己),违反SRP(单一责任原则)等设计缺陷。

如果为了通过测试而为测试编写传递代码,那么您已经失败了:您应该将难以编写测试视为可以让您问的路标:为什么这样做办法?为什么我不能在不依赖其他代码的情况下测试此代码?为什么我不能重用这段代码?为什么这个代码在单独使用时会破坏?

此外,如果您的设计真正干净,并且您的代码真正可维护,为什么为它编写测试并不容易?

答案 1 :(得分:13)

总是存在过度使用TDD设计或前期设计的风险。所以答案是它取决于。我更喜欢从用户故事/验收测试开始,这是我的测试有助于生成的要求的基础。只有在我建立之后,我才开始编写TDD风格的详细单元测试。如果你所做的唯一设计和思考是通过TDD,那么你冒着太多的自下而上的方法,这可能会给你单独和类别隔离的优秀,但当你尝试将它们集成到用户故事中完成任务时做错了可能会感到惊讶。如需更多灵感,请查看BDD

罗伯特·C·马丁和詹姆斯·科普利恩之间的A great "debate" about this has been recorded,前者是TDD的倡导者,而后者则声称它破坏了系统的设计。这就是罗伯特关于TDD和设计的说法:

  

“敏捷中有一种感觉   社区自'99年左右开始   架构是无关紧要的,我们没有   需要做建筑,我们需要的一切   要做的就是写很多测试并做   很多故事,做得快   迭代和代码将汇编   神奇地,这一直是   马屎。我甚至认为大多数   最初的敏捷支持者会   同意这是一种愚蠢。“

James Coplien表示,仅仅从TDD推动您的设计风险很大:

  

“我们在很多方面看到了很多东西   很多项目,就是那个项目   南部关于他们的第三次冲刺和   他们崩溃和燃烧,因为他们   不能再进一步了,因为他们   已经走投无路了   建筑。你不能   重构你的方式,因为   重构必须跨越阶级   类,跨类层次结构,   你不再拥有任何东西了   关于拥有相同的保证   功能“。

此外,他还提供了一个很好的例子,说明如果您使用您的前期知识来推动架构,那么如果您测试推出银行帐户可能会看起来如何:

  

“我记得在和我说话的时候   肯特曾经,在早期   当他提出TDD时,这个   是在YAGNI和做的意义上   最简单的事情   工作,他说:'好的。让我们做一个   银行账户,一个储蓄账户。'   什么是储蓄账户?它是   号码,您可以添加到号码   你可以从数字中减去。   那么储蓄账户是什么呢?   计算器。我们来做一个计算器,   我们可以证明你可以加入   平衡和减去   平衡。这是最简单的事情   一切都可能奏效   否则就是这种演变。

     

如果你做一个真正的银行系统,a   储蓄账户甚至不是一个对象   而且你不会重构你的   通往正确架构的方法   那个。什么是储蓄账户,   是一个进行迭代的过程   通过数据库的审计跟踪   交易,存款和利息   聚会和其他转变   钱。这不像储蓄   帐户是坐在上面的一些钱   尽管如此,某处某处的银行货架上   这是用户的观点,和   你必须知道有   这些相对错综复杂的结构   在银行系统的基础上   支持税务人员和   精算师和所有这些其他人,   那是你无法进入的   增量方式。好吧,你可以,   因为当然是银行业   40年后来到这里。您   想给自己40年?它的   不灵活。“

有趣的是,TDD支持者和TDD对手都说你需要预先设计。

如果您有时间,请观看视频。这是两位极具影响力的专家之间的精彩讨论,而且只有22分钟。

答案 2 :(得分:4)

我完全同意pjz。没有一种正确的方法来设计软件。如果你把TDD推向极端,除了下一次单元测试之外没有任何预见,你可能会让自己变得更加困难。同样花费数月时间在图表和文档上,但没有代码,开始了一个盛大的软件项目的人。

中等。如果您想要绘制一个快速图表来帮助您查看代码结构,那就去吧。如果您需要两个页面,可能是时候开始编写一些代码了。如果你想在编写测试之前这样做,那么什么。目标是工作,质量软件,而不是绝对符合任何特定的软件开发原则。做对你和你的团队有用的东西。找到可以进行改进的区域。迭代。

答案 3 :(得分:3)

我完全同意你的观点。在 practice 中我认为TDD经常会对代码库产生一些非常负面的影响(糟糕的设计,程序代码,没有封装,生产代码乱七八糟的测试代码,界面无处不在,难以重构生产代码,因为一切都是紧密耦合到许多测试等。)。

Jim Coplien已经就此主题进行了一段时间的讨论:

  

最近的研究(Siniaalto和   TDD的Abrahamsson表示可能   没有传统的好处   测试 - 最后的发展和一些   案件使代码恶化   它有其他惊人的(他们的   字)效果。那个让我担心的人   最多的是它恶化了   建筑。    - Jim's blog

罗伯特·C·马丁和詹姆斯·科普利恩之间也有一个discussion over on InfoQ,他们会谈到这个问题。

答案 4 :(得分:3)

我想到的方法是,首先写下你希望你的代码看起来像什么。 一旦你有了目标代码的样本(现在什么也没做),看看你是否可以在它上面放置一个测试脚手架。 如果你不能这样做,找出你不能做到的原因。 大部分时间都是因为你做出了糟糕的设计决定(99%),但如果情况不是这样(1%),请尝试以下方法:

  • 确定您需要遵守的疯狂要求,不要让您测试代码。您了解重新设计API的问题。
  • 如果其他人决定此要求,则讨论他/她。他们可能有充分的理由要求,一旦你知道他们的原因,你就能完善你的设计并使其可测试。如果不是现在你可以重新修改要求,你会两个都做得更好。

获得目标代码和测试脚手架后。实现代码。现在,您甚至可以通过自己的测试了解自己的进步情况(这是一个很好的激励因素!)

根据个人经验,测试可能是多余的唯一情况是,当您制作早期原型时,因为此时您仍然不能很好地理解问题,无法准确地设计或测试您的代码。

答案 5 :(得分:2)

完成软件有三个步骤:

  1. 让它发挥作用
  2. 正确
  3. 快点
  4. 测试让你#1。您的代码不是因为测试已通过而完成的。在开始编写测试/代码之前,最好有项目结构的概念(实用程序,常用对象,层,框架)。在编写代码以使测试通过之后,您需要重新评估它以查看哪些部分可以重构为应用程序的不同方面。 Yuo可以自信地做到这一点,因为您知道只要您的测试仍在通过,您的代码仍然可以正常运行(或者至少满足要求)。

    在项目开始时,请考虑结构。随着项目的继续进行评估和重新评估您的代码以保持设计到位或更改设计,如果它停止有意义。当您估计时,所有这些项目都必须考虑在内,或者您最终会得到spagetti代码,TDD与否。

答案 6 :(得分:1)

总是平衡:
  - 太多的TDD,你最终得到的代码可行,但是很难处理   - 过多的'维护代码,干净的设计和完善的架构',你最终得到了Architecture Astronauts,他们已经谈到编码瘫痪

万事万物。

答案 7 :(得分:1)

我对TDD和单元测试比较陌生,但在我使用它的两个项目中,我发现它是一个设计 aide 而不是设计的替代品。独立测试和验证组件/子组件的能力使我更容易进行快速更改并尝试新的设计理念。

我对TDD的不同之处在于可靠性。在设计过程开始时,而不是在以后,在较小级别的组件上处理组件接口的过程是,我有可以信任的组件之前,所以我可以不用担心关于小件,而是开始解决棘手的问题。

当我不可避免地需要回来维护小件时,我可以花费时间这样做,所以我可以回到我想要做的工作。

答案 8 :(得分:1)

在大多数情况下,我同意TDD确实提供了一种设计工具。对我来说,最重要的部分是它能够进行更多更改(你知道,当你有一个洞察力的时刻,你可以通过删除代码添加功能),并大大降低了风险。

尽管如此,我最近签约的一些算法工作在TDD下遭受了一些影响而没有仔细平衡设计思想。关于更安全的重构的上述陈述仍然是一个很大的好处,但对于某些算法,TDD(虽然仍然有用)不足以让您找到理想的解决方案。以排序为例。 TDD很容易引导您进入次优(N ^ 2)算法(以及允许您重构快速排序的大量传递测试),如冒泡排序。 TDD是一个非常好的工具,但是很多东西需要适当地用于解决问题的背景。

答案 9 :(得分:1)

这里有许多非正式的意见,包括流行的意见(来自Jon Limjap),错误的结果来自错误,并且声称似乎没有个人经验支持。优势经验证据和公布的结果指出了与该经验相反的方向。

理论是一种方法,它要求您在代码之前编写测试,这将导致在单个代码片段的层次上思考设计 - 即,在小编程中。由于所有程序都可以测试(您仍然可以一次测试一个对象,并且您根本无法在大多数语言中测试类),因此您的设计重点将放在各个方法及其组成方式上。从理论上讲,这导致自下而上的程序设计,反过来导致对象之间的不良耦合和内聚。

广泛的经验数据证实了这一理论。 Siniaalto和Abrahamsson(关于测试驱动开发对程序设计和测试覆盖的影响的比较案例研究),ESEM 2007,发现“我们的结果表明内聚可能更糟(即使Beck声称TDD产生了高度凝聚力的系统。在我们的第二项研究中,我们注意到TDD的复杂度测量值更好,但依赖管理指标明显更差。“ Janzen和Saledian(测试驱动开发真的能提高软件设计质量吗? IEEE Software 25(2),2008年3月/ 4月,第77-84页)发现“他的结果没有” t支持声称更低耦合和增加内聚与TDD“。

文献综述将揭示推进这些案例的其他出版物。

即便是我亲爱的朋友Bob叔叔写道:“敏捷开发的一个更为阴险和持久的神话是前期架构和设计都很糟糕;你不应该花时间预先做出架构决策。相反,你应该这样做从零开始改进你的架构和设计,一次一个测试用例。原谅我,但那是马屎。“ (“敏捷建筑的Scatology”, http://blog.objectmentor.com/articles/2009/04/25/the-scatology-of-agile-architecture

然而,值得注意的是,更广泛的失败是人们认为它是一种测试技术而不是设计技术。 Osherov指出了许多方法,这些方法通常与TDD等同。我无法确定这里的海报是什么意思。请参阅:http://weblogs.asp.net/rosherove/archive/2007/10/08/the-various-meanings-of-tdd.aspx