it-swarm.cn

TDD为什么起作用?

测试驱动开发(TDD) 如今很大。在Programmers SE和其他场所,我经常看到它被推荐作为解决各种问题的解决方案。我不知道为什么行得通。

从工程角度来看,这使我感到困惑,原因有两个:

  1. “编写测试+重构直到通过”方法看起来令人难以置信的反工程。例如,如果土木工程师使用该方法进行桥梁建造,或使用汽车设计师作为汽车制造商,则他们将以很高的成本重塑桥梁或汽车,结果将是没有经过深思熟虑的体系结构造成的混乱。 “重构直到通过”准则通常被视为忘记架构设计并执行只要有必要以符合测试的要求。换句话说,测试而不是用户来设置需求。在这种情况下,我们如何保证结果中的良好“缺陷”,即最终结果不仅是正确的,而且是可扩展的,健壮的,易于使用的,可靠的,安全的,安全的等?这就是架构通常要做的。
  2. 测试不能保证系统正常运行。它只能表明没有。换句话说,测试可能会向您显示如果系统未通过测试,则该系统包含缺陷,但是通过所有测试的系统并不比未通过测试的系统安全。测试覆盖率,测试质量和其他因素在这里至关重要。在民用和航空航天业中,已经报道了“绿色”结果给许多人带来的错误安全感觉,这是极其危险的,因为它可能被解释为“系统还不错”,而当它真正意味着“系统还不错时”。作为我们的测试策略”。通常,不检查测试策略。或者,谁来测试测试?

总而言之,我更关心TDD中的“驱动”位而不是“测试”位。测试完全可以;我没有得到的是通过设计来驱动设计。

我想看到一些答案,这些答案包含了为什么软件工程中的TDD是一种好的做法以及为什么我上面解释的问题与软件无关(或不足够相关)的原因。谢谢。

93
CesarGon

我认为这里有一个误解。在软件设计中,设计与产品非常接近。在土木工程,建筑设计中,设计与实际产品是分离的:存在保留设计的蓝图,然后将这些蓝图具体化为最终产品,并且将这些蓝图与大量的时间和精力分开。

TDD正在测试设计。但是,每个汽车设计和建筑设计也都经过测试。首先计算建筑技术,然后以较小规模进行测试,然后以较大规模进行测试,然后再将其发布到实际建筑物中。例如,当他们发明H型梁和负载时,请放心,在实际使用它建造第一座桥之前,已经对其进行了尝试和尝试。

还可以通过设计原型来测试汽车的设计,是的,当然可以通过调整不完全正确的方法来进行测试,直到达到期望。但是,此过程的一部分速度较慢,因为如您所说,您对产品的了解不多。但是,每一辆汽车的重新设计都借鉴了以前的经验,每座建筑物在空间,光线,绝缘,强度等方面的重要性背后都有数千年的基础。建筑物中的细节都在不断变化和改进。并针对新版本进行重新设计。

此外,还对零件进行了测试。也许与软件的样式并不完全相同,但是通常会测量机械零件(车轮,点火器,电缆)并施加压力,以了解尺寸是否正确,没有异常现象发生,等等。它们可能会被X射线或激光照射,测量时,他们会敲击砖块来发现破裂的砖块,可能会在某种配置下对它们进行实际测试,或者对大型组进行有限的表示以将其真正用于测试。

这些就是您可以使用TDD放置的所有内容。

确实,测试不能保证。程序崩溃,汽车抛锚,当风吹来时,建筑物开始做有趣的事情。但是...“安全性”不是布尔值问题。即使您无法涵盖所有​​内容,也可以覆盖-例如-99%的可能性比仅覆盖50%的可能性要好。如果不进行测试,然后发现钢的沉降不好,就会变脆,刚放上主结构时,锤子第一次敲击就会断裂,这简直是浪费金钱。还有其他可能仍会伤害建筑物的问题,并不能使它变得如此愚蠢,以至于可以轻易预防的缺陷使您的设计下降。

对于TDD的实践,这是一个平衡问题。以一种方式进行操作的成本(例如,不进行测试,然后再整理零件),而以另一种方式进行操作的成本。它始终是一种平衡。但是不要认为其他设计过程没有适当的测试和TDD。

66
Inca

IMO,TDD的大多数成功案例都是伪造的,只是出于营销目的。它可能很少成功,但仅适用于小型应用程序。我正在使用TDD原理的大型Silverlight应用程序上工作。该应用程序已进行了数百次测试,但仍不稳定。由于复杂的用户交互,应用程序的某些部分无法测试。产生的测试带有大量的模拟和难以理解的代码。

最初,当我们尝试TDD时,一切似乎都很好。我能够编写很多测试,并模拟出难以进行单元测试的部分。一旦有了足够多的代码并且需要更改接口,您就会被搞砸。需要修复许多测试,并且您将重写比实际更改代码更多的测试。

彼得·诺维格(Peter Norvig)在《编码员在工作》一书中解释了他对TDD的看法。

Seibel:使用测试驱动设计的想法怎么样?

Norvig:我将测试更多地看作是纠正错误的一种方式,而不是一种设计方式。这种极端的说法是:“首先,您要做的是编写一个测试,说最后我得到了正确的答案”,然后运行它,发现它失败了,然后您说:“我该怎么办?还需要下一步吗?”,这似乎不是为我设计东西的正确方法。看起来,只有如此简单以至于预先确定解决方案才有意义。我认为您必须首先考虑它。您必须说:“碎片是什么?在不知道其中的一些内容之前,我该如何为它们编写测试?”然后,一旦完成,对每个部分进行测试并很好地理解它们如何相互影响以及边界情况等等,这是很好的纪律。这些都应该进行测试。但是我不认为您会说“此测试失败了”。

26
Navaneeth K N

测试驱动设计对我有用,原因如下:

它是规范的可运行形式。

这意味着您可以从测试用例中看到:

  1. [〜#〜] [〜#〜]代码被完全填充了规范,因为期望的结果就在测试用例中。目视检查(期望测试用例通过)可以立即说“哦,该测试检查给定发票invoiceCompany的这种情况,应该有那个结果”。
  2. [〜#〜]如何[〜#〜]应该调用代码。无需任何外部支架即可直接指定进行测试所需的实际步骤(模拟数据库等)。

您首先从外部编写视图。

通常,您以首先解决问题的方式编写代码,然后您想到如何调用刚刚编写的代码。这通常会给用户带来尴尬的界面,因为通常更容易“仅添加标志”等。考虑到“我们需要这样做,使测试用例看起来像那样”,您就可以解决这个问题。这将提供更好的模块化,因为代码将根据调用接口而不是相反的方式编写。

通常,这也将导致代码更简洁,从而需要较少的解释性文档。

你做得更快

由于您具有可运行形式的规范,因此在完整的测试套件通过后就可以完成。在更详细地阐明事物时,您可以添加更多测试,但是作为基本原理,您将获得非常清晰可见的进度指示器以及完成时间。

这意味着您可以告诉您什么时候有必要做某事(它有助于通过测试),最终您需要做的事少了。

对于那些对它可能有用的人,我鼓励您在下一个库例程中使用TDD。慢慢建立可运行的规范,并使代码通过测试。完成后,所有需要了解如何调用库的人都可以使用可运行的规范。

最近的研究

“案例研究的结果表明,相对于未使用TDD做法的类似项目,这四种产品的预发布缺陷密度降低了40%至90%。从主观上讲,团队的产品发布前缺陷密度增加了15%至35%。采用TDD后的初始开发时间。” 〜 4个工业团队的结果和经验

25
user1249

创建软件的过程不是编写代码的过程。没有“广泛的范围”计划,任何软件项目都不应首先开始。就像跨河两岸的项目一样,首先需要这样的计划。

TDD方法(主要)与单元测试有关-至少这是人们倾向于考虑的方式-这是创建软件代码的最低级位。当已经定义了所有功能和行为并且我们实际上知道我们想要实现的目标时。

在结构工程中,它看起来像这样:

``我们将这两块金属连接在一起,并且该连接需要承受x数量级的剪切力。让我们测试哪种连接方法是执行此操作的最佳方法。

为了测试软件是否可以整体运行,我们设计了其他类型的测试,例如可用性测试,集成测试和验收测试。这些也应该在编写代码的实际工作开始之前定义,并且在单元测试为绿色之后执行。

请参见V模型: http://en.wikipedia.org/wiki/V-Model_%28software_development%29

让我们看看它如何在桥梁上工作:

  1. 地方政府对一家桥梁建筑公司说:“我们需要一座桥梁来连接这两个点。桥梁需要每小时能够允许n的交通量,并且要在2012年12月21日之前准备就绪”-这是对如果验收测试无法通过,公司将不会获得全额(或任何)款项。

  2. 公司的管理决定项目进度。他们建立工作团队并为每个团队设定目标。如果团队无法实现这些目标-桥梁将无法按时建成。但是,这里有一定程度的灵活性。如果其中一个团队遇到问题,公司可以通过更改需求,更改分包商,雇用更多人员等来弥补这一点,从而使整个项目仍达到第1点中设定的目标。

  3. 在负责设计特定桥梁组件的团队中,就像我在上面的示例中所示。有时解决方案是显而易见的,因为我们拥有大量有关搭建桥梁的知识(就像在软件开发中使用经过良好测试的库一样-您仅假设它按宣传的方式工作)。有时您需要创建多个设计并对其进行测试以选择最佳设计。仍然要事先知道测试组件的标准。

19
Mchl

在我看来,TDD之所以有效,是因为

  • 它会迫使您在决定实施之前,以所需的精度定义单位要执行的操作,而该精度水平通常未包含在任何规格或要求文档中
  • 它使您的代码具有固有的可重用性,因为您必须在测试和生产场景中都使用它
  • 它鼓励您以较小的难度编写代码来测试块,这往往会导致更好的设计

特别是关于你提出的要点

  • 代码比砖或钢更具延展性,因此修改便宜。如果您进行测试以确保行为不变,则价格仍然便宜
  • TDD并非不做设计的借口-仍然建议使用高级体系结构,只是不要过多地进行细节设计。不鼓励进行Big Up Front设计,但鼓励进行足够的设计
  • TDD不能保证系统能正常工作,但是它可以防止许多小错误,否则这些小错误就会被遗漏。另外,由于它通常会鼓励使用更好的分解代码,因此通常更容易理解,因此不太容易出错
18
Gavin Clarke

TL; DR

编程仍然是设计活动,不是构造。在事实发生之后编写单元测试只能确认该代码已完成其工作,而不是确定它已完成了一些有用的工作。测试失败是真正的价值,因为它们可以让您及早发现错误。

代码就是设计

[〜#〜] ppp [〜#〜] 的第7章中,“鲍勃叔叔”直接讨论了这个问题。在本章的开始,他引用了Jack Reeves的 优秀文章 ,他在其中提出了代码是设计的(链接指向收集有关该主题的所有三篇文章的页面)。

关于这一论点,有趣的是,他指出,与其他工程学科的建设活动非常昂贵相比,软件的构建是相对免费的(在您的IDE如果您将编写代码视为设计活动而不是构造活动,那么红绿重构周期基本上就是设计工作,您的设计会随着编写测试,满足测试要求的代码并重构为测试而发展。将新代码集成到现有系统中。

TDD作为规范

您理解为TDD编写的单元测试是对规范的直接翻译。通过编写最低限度满足您的规范的代码(使测试变为绿色),您编写的所有代码都将用于特定目的。通过重复测试可以验证该目的是否得到满足。

对功能进行测试

单元测试中的一个常见错误是,您在代码之后编写测试,最终对代码执行的工作进行了测试。换句话说,您将看到这样的测试

public class PersonTest:Test
{
   [Test]
   TestNameProperty()
   {
      var person=new Person();
      person.Name="John Doe";
      Assert.AreEqual("John Doe", person.Name);
   }
}

虽然我认为这段代码可能很有用(请确保某人没有通过简单的属性进行淫秽的操作)。它不能用来验证规范。正如您所说,编写此类测试只会带您走那么远。

虽然绿色很好,但价值在于红色当我遇到意外的测试失败时,我在TDD中遇到了第一个真正的“ aha”时刻。我有一套针对正在构建的框架的测试。添加一个新功能后,我为此进行了测试。然后编写代码以使测试通过。编译,测试...在新测试上获得绿色。但是在另一个我没想到会变红的测试中也变红了。

看着失败,我松了一口气,因为如果我没有适当的测试,我怀疑我会在相当长的一段时间内发现该错误。这是一个非常讨厌的错误。幸运的是,我进行了测试,它准确地告诉了我修复该错误所需的操作。如果没有测试,我将继续构建我的系统(该错误会感染依赖于该代码的其他模块),并且在发现该错误时,正确修复它将是一项主要任务。

TDD的真正好处在于,它使我们能够毫不留情地进行更改。就像编程的安全网一样。想想如果空中飞人失误跌倒会发生什么。使用网络,这是一个令人尴尬的错误。没有,这是一个悲剧。同样,TDD可以使您避免将愚蠢的错误变成项目致命的灾难。

16
Michael Brown

您找不到任何提倡测试驱动开发,甚至提倡测试驱动Design(他们不同)的人,这表示测试证明了应用程序。因此,让我们称其为稻草人即可。

您不会发现任何不喜欢TDD或没有对TDD印象深刻的人,它说测试是浪费时间和精力。尽管测试不能证明应用程序,但是它们对于发现错误很有帮助。

说了这两点,对于在软件上实际执行测试,双方都没有做任何不同的事情。两者都在做测试。两者都依靠测试来发现尽可能多的错误,并且都使用测试来验证软件程序是否正常运行以及是否可以在当时被发现。没有半点头绪的人会在没有测试的情况下出售软件,没有半点头绪的人不会期望测试将使他们出售的代码完全没有错误。

因此,TDD和not-TDD之间的区别不是正在进行测试。区别在于编写测试的时间。在TDD中,测试是在软件之前编写的。在非TDD中,测试是在软件之后或与软件配合进行的。

我看到的关于后者的问题是,测试然后倾向于将目标对准编写的软件,而不是期望的结果或规范。即使测试团队与开发团队是分开的,测试团队也倾向于查看软件,使用它并编写针对该软件的测试。

研究项目成功的人一次又一次注意到的一件事是,客户多久会提出他们想要的东西,开发人员跑出来写点东西,并且当他们回到客户那里说“完成”时事实证明,这完全不是客户要求的。 “但是它通过了所有的测试……”

TDD的目标是打破这种“循环论证”,并为测试不是软件本身的软件的测试提供基础。编写测试以针对“客户”想要的行为。然后编写该软件以通过那些测试。

TDD是旨在解决此问题的解决方案的部分。这不是您迈出的唯一一步。您需要做的其他事情是确保有更多的客户反馈并且更频繁。

但是以我的经验来看,TDD是成功实施非常困难的事情。在产品推出之前很难编写测试,因为许多自动化测试都需要进行一些操作才能使自动化软件正常工作。要使不习惯单元测试的开发人员也很难做到这一点。我一次又一次地告诉团队中的人首先编写测试。我从来没有真正做到这一点。最后,时间限制和政治破坏了所有努力,因此我们甚至不再进行单元测试。当然,这不可避免地导致了设计的偶然性和严重的耦合,因此,即使我们愿意,现在实施起来的成本也非常高。 TDD最终为开发人员提供了避免THAT。

11
Edward Strange

首先设计

TDD是不是跳过设计的借口。我已经看到许多“敏捷”潮流中的飞跃,因为它们虽然可以立即开始编码。真正的敏捷将使您进行状态编码的速度快于启发瀑布过程的(其他领域)工程良好实践。

但是尽早测试

当有人说测试正在推动设计时,它仅表示人们可以在设计阶段的很早就使用测试,而这要早于完成。进行此测试将对产品的灰色区域提出挑战,并将其与现实世界相提并论,从而大大影响您的设计,而这要早于产品完成。迫使您经常回到设计并对其进行调整以考虑到这一点。

测试和设计...一模一样

在我看来,TDD只是将测试作为设计的integral部分,而不是最后进行验证。随着您开始越来越多地使用TDD,您会在设计时就着手解决如何破坏/破坏系统的想法。我个人并不总是先进行测试。当然,我在接口上进行了明显的(单元)测试,但是真正的收获来自于我想到这种设计可以打破的新颖且创造性的方式时所创建的集成和规范测试。我一想到一种方法,便为它编写了一个测试代码,然后看看会发生什么。有时我可以忍受这种后果,在这种情况下,我将测试移到一个不属于主构建的单独项目中(因为它将继续失败)。

那谁来主持演出呢?

在TDD中,此处的驱动只是意味着您的测试对设计的影响如此之大,以至于您可以感觉到他们实际上在驱动它。但是,到此为止,我明白您的担忧,这有点吓人...谁来主持这场演出?

[〜#〜] you [〜#〜]正在驾驶,而不是测试。测试在那里进行,因此随着您的前进,您对所创建的内容会充满信心,从而使您进一步了解它基于扎实的基础。

只要测试是可靠的就可以

完全,因此在TDD中被驱动。测试并不是驱动整个事情的重要因素,但是它们将对您的工作方式,设计和思维方式产生深远的影响,因此您会将很大一部分思维过程委托给测试,并以此作为回报。它们将对您的设计产生深远的影响。

是的,但是如果我用我的桥来做...

停在这里...软件工程与那里的其他工程实践完全不同。实际上,软件工程实际上与文献有很多共同点。一个人可以读一本完整的书,从中撕掉4章,然后写两个新的章来代替它们,将它们重新粘贴在书中,您仍然会拥有一本好书。有了良好的测试和软件,您可以撕裂系统的任何部分并用另一部分替换,这样做的成本并不比它最初创建时要高得多。实际上,如果您进行了测试并允许它们充分影响您的设计,则它可能比最初创建它会便宜得多,因为您将有一定程度的信心,认为这种替代不会破坏测试涵盖的范围。

如果它太好了,怎么不总是起作用?

因为测试与构建所需要的思维方式非常不同。并非每个人都能来回切换,实际上,有些人将无法仅仅因为无法下定决心破坏自己的创作而建立适当的测试。这将导致项目进行的测试太少或测试不足以达到目标指标(想到代码覆盖率)。他们会乐于进行路径测试和异常测试,但会忘记极端情况和边界条件。

其他人将仅依靠部分或全部放弃测试的测试。每个成员都这样做,然后彼此整合。设计首先是一种沟通工具,我们在地上树立的标语是我将要去的地方,草图则是这是门窗将要去的地方。没有此功能,无论您进行多少测试,您的软件都将注定失败。集成和合并将一直很痛苦,并且它们将缺少最高抽象级别的测试。

这些团队TDD可能并不是要走的路。

9
Newtopian

使用TDD时,您往往不会编写难以或快速测试的代码。这看起来似乎是一件小事,但它可能会对项目产生深远的影响,因为它会影响重构,测试,重现测试错误和验证修复的难易程度。

当您具有更好的测试支持的分解代码时,项目中的新开发人员也可以更快地上手。

7
Alb

尽管我自己没有那么多地练习TDD,但我一直在考虑很多。代码质量与后续TDD之间似乎存在(强?)正相关。

1)我的第一个观点是,这(主要)不是因为TDD在代码中(因此)添加了“更好的质量”,更像是TDD帮助清除了最差的部分和习惯,从而间接地提高了质量。

我什至主张,这不是测试本身,而是写作这些测试的过程。很难为错误的代码编写测试,反之亦然。并在编程时将其放在首位,消除了许多不良代码。

2)另一种观点(这是哲学上的)是遵循主人的心理习惯。您不会通过遵循他的“外部习惯”来学习成为大师(例如,留胡子的习惯是好的),您必须学习他的内部思维方式,这很难。使(新手)程序员遵循TDD,使他们的思维方式更接近于大师。

5
Maglob

“编写测试+重构直到通过”方法看起来令人难以置信的反工程。

您似乎对重构和TDD都有误解。

代码重构 是更改计算机程序源代码而不修改其外部功能行为以改善软件的某些非功能属性的过程。

因此,在通过之前,您无法refactor代码。

TDD,特别是单元测试(我认为是核心的改进,因为其他测试对我来说似乎很合理),并不是在重新设计组件之前就可以进行工作。它是关于设计组件并进行实现直到组件按设计工作为止。

真正掌握同样重要的是,nit测试就是关于测试nits。由于总是从头开始编写很多东西的趋势,因此测试此类单元很重要。一位土木工程师已经知道他使用的单元的规格(不同的材料),并且可以期望它们能够工作。这是两件事,通常不适用于软件工程师,并且在使用它们之前对它们进行非常专业的工程设计,因为这意味着要使用经过测试的高质量组件。
如果土木工程师有想法使用一些新的纤维纸来制作覆盖体育场的屋顶,那么您希望他将其作为一个单元进行测试,即定义所需的规格(例如重量,渗透性,稳定性)等),然后对其进行测试和优化,直到符合要求为止。

这就是TDD起作用的原因。因为如果您构建经过测试的单元的软件,则将其工作的机会要大得多,将它们连接在一起时,如果没有,则可以认为问题出在您的粘合代码中,前提是您的测试覆盖范围广。

编辑:
重构意味着:没有功能更改。编写单元测试的一点是要确保重构不会破坏代码。因此,TDD的目的是确保重构不会产生副作用。
粒度不是透视的主题,因为正如我所说,单元测试是测试单元而不是系统,因此可以精确定义粒度。

TDD鼓励良好的体系结构。它要求您为所有单元定义和实现规范,迫使您在实现之前设计它们,这与您似乎想的完全相反。 TDD规定了单元的创建,这些单元可以单独测试,因此可以完全解耦。
TDD并不意味着我对意大利面条代码进行了软件测试,并搅拌通心粉直至通行。

与土木工程不同,在软件工程中,项目通常会不断发展。在土木工程中,您需要在位置A建造一座桥梁,该桥梁可以承载x吨,并且足够宽,每小时可容纳n辆汽车。
在软件工程中,客户基本上可以在任何时候决定(可能是在完成之后),他想要一个双层桥,并且希望它与最近的高速公路连接,并且他希望它是一个起重平台大桥,因为他的公司最近开始使用帆船。
软件工程师的任务负责更改设计。不是因为他们的设计有缺陷,而是因为那是作案手法。如果软件设计良好,则可以在不重新编写所有底层组件的情况下,从高层次进行重新设计。

TDD是关于使用经过单独测试的,高度分离的组件来构建软件的。如果执行得当,它将比不使用它来帮助您更快,更安全地应对需求变化。

TDD在开发过程中增加了要求,但并不禁止任何其他质量保证方法。诚然,TDD不能提供与形式验证相同的安全性,但是同样,形式验证非常昂贵,并且无法在系统级别使用。而且,如果您愿意,可以将两者结合起来。

TDD还包含在系统级别执行的单元测试以外的测试。我发现这些内容很容易解释,但难以执行且难以衡量。而且,它们很合理。尽管我绝对看到它们的必要性,但我并没有真正将它们视为思想。

最后,没有工具能够真正解决问题。工具只会使解决问题更加容易。您可以问:凿子将如何帮助我打造出色的建筑?好吧,如果您打算做直墙,直砖会有所帮助。是的,当然,如果您将该工具提供给白痴,他可能最终会用脚猛击它,但这不是凿子的错,这并不是TDD的缺陷,因为它给新手提供了虚假的安全保障,谁没有写好的测试。
因此,最重要的是,可以说TDD的工作比没有TDD的要好得多。

3
back2dos

我认为您正在从错误的角度接近第一个问题。

从理论上讲,我们通过检查故障点来证明某些方法可行。那就是使用的方法。您可能有许多其他方法可以证明某项功能正常,但是TDD的建立是由于其按位方式的简单性:如果不破坏,它将起作用。

实际上,这直言不讳地转换为:现​​在我们可以继续进行下一件事情(在成功应用TDD满足所有谓词之后)。如果您从这种角度看待TDD,那么这与“编写测试+重构直到通过”无关,而更多地是关于已经完成了这一点,我现在将重点全放在下一个功能上重要的事情

考虑一下这如何适用于土木工程。我们正在建设一个体育场,可容纳15万人。在证明体育场的结构完整性是合理的之后,我们满足了安全第一。现在,我们可以集中精力处理其他紧迫的问题,例如洗手间,食品摊位,座位等,从而使听众的体验更加愉悦。这是一个过分的简化,因为TDD还有很多其他功能,但症结在于,如果您同时专注于令人兴奋的新功能并保持完整性,那么您将无法获得最佳的用户体验。在这两种情况下,您都会获得成功。我的意思是,您怎么能确切知道how许多厕所,您应该在哪里放置15万人?我很少见到体育场在我的一生中倒塌,但我不得不在半场半场排队等候很多次。那就是说洗手间问题可以说更复杂,如果工程师们可以花更少的时间在安全上,他们也许最终能够解决洗手间问题。

您的第二点无关紧要,因为我们已经同意绝对值是 愚蠢的努力 ,并且因为Hank Moody说它们不存在(但我似乎找不到关于它的参考)。

2
Filip Dupanović

我不喜欢您说“测试而不是用户来确定要求”。我认为您只考虑在TDD中进行单元测试,而其中还涉及集成测试。

除了测试构成软件基础的库之外,还要编写覆盖用户与软件/网站/所有内容的交互的测试。这些直接来自用户,像Cucumber(http://cukes.info)这样的库甚至可以让您的用户以自然语言自己编写测试。

TDD还鼓励代码的灵活性-如果您花很多时间设计某些东西的体系结构,那么以后在必要时进行这些更改将非常困难。首先编写一些测试,然后编写一些通过这些测试的代码。添加更多测试,添加更多代码。如果您需要从根本上更改代码,则测试仍然有效。

与桥梁和汽车不同,单个软件在其生命周期内可能会发生巨大变化,并且无需先编写测试就可以进行复杂的重构,只是麻烦而已asking

2
sevenseacat

如果您接受发现的bug越早,修复它们的成本越低,那么仅此一项就使TDD值得。

1
SnoopDougieDoug

软件工程中的TDD是一种好习惯,与应用程序中的错误处理以及日志记录和诊断一样(尽管它是错误处理的一部分)也是一种好习惯。

TDD不能用作将软件开发减少为试验和错误编码的工具。但是,大多数程序员仍然在开发阶段盯着运行时日志,观察调试器中的异常情况或使用其他失败/成功的迹象,其中包括整天编码/编译/运行应用程序。

TDD只是使这些步骤正式化和自动化的一种方法,可以使您作为开发人员更加高效。

1)您无法将软件工程与桥梁建设相提并论,桥梁建设的灵活性与设计软件程序的灵活性相差无几。构造网桥就像将相同的程序一遍又一遍地写入有损机器中。网桥不能像软件那样重复和重用。每个桥都是唯一的,必须制造。汽车和其他设计也是如此。

在软件工程中,最困难的事情是重现故障,当网桥发生故障时,通常很容易确定出了什么问题,从理论上讲,重现故障也很容易。当计算机程序失败时,它可能是一系列复杂的事件,使系统进入故障状态,并且很难确定错误的位置。 TDD和单元测试使测试软件组件,库和算法的健壮性变得更加容易。

2)使用弱单元测试和浅测试用例,它们不会使系统产生虚假的信心,这只是一种不好的做法。当然,忽略系统的体系结构质量并仅仅完成测试也是很糟糕的。但是在建筑工地作弊以节省摩天大楼或桥梁以节省材料而又不遵循设计蓝图是很糟糕的,并且一直在发生…….

1
Ernelli

我给你一个简短的答案。通常,TDD就像单元测试一样,以错误的方式看待。直到最近,我在观看了精彩的技术讲座视频后才了解单元测试。从本质上讲,TDD只是在说明您希望以下工作。必须实施。然后,您可以按照通常的方式设计其余的软件。

有点像在设计库之前为库编写用例。除了可以在库中更改用例之外,您可能不使用TDD(我将TDD用于API设计)。还鼓励您添加更多测试,并考虑该测试可能获得的狂放投入/使用。在编写库或API时,我发现它很有用,如果您更改某些内容,则必须知道自己已破坏某些内容。在大多数日常软件中,我都不会打扰,因为为什么我需要一个供用户按下按钮的测试用例,或者为什么我要接受CSV列表或每行只有一个条目的列表,所以我没关系。改变它,因此我不应该/不能使用TDD。

0
user2528

TDD为什么起作用?

没有。

澄清:自动化测试总比没有测试好。但是我个人认为,大多数单元测试都是浪费的,因为它们通常是重言式的(即说从实际测试代码中可以明显看出来),并且不能轻易证明它们是一致的,不是多余的并且涵盖所有边界情况(通常会发生错误) )。

最重要的是:优秀的软件设计并没有像许多敏捷/ TDD推广者所宣传的那样神奇地从测试中消失。否则,每个人都请提供指向经过同行评审的科学研究的链接,以证明这一点,或者至少引用一些开源项目,在该项目中,可以通过其代码更改历史来潜在地研究TDD的益处。

0
KolA

TDD与测试无关。当然,它不能代替良好的测试。它给您的是design,它经过深思熟虑,易于消费者使用,并且易于以后维护和重构。这些事情反过来导致更少的错误和更好,更易适应的软件设计。 TDD还可以帮助您仔细考虑并记录您的假设,通常会发现其中一些不正确。您会在流程的早期发现这些。

作为一个不错的附带好处,您可以运行一大套测试,以确保重构不会改变软件的行为(输入和输出)。

0
Marcie

当结构工程是具体的时,软件是有机的。

当您构建桥梁时,它仍将是一座桥梁,并且不太可能在短时间内演变成其他事物。改进将持续数月和数年,但不会像软件一样耗时数日。

单独进行测试时,通常可以使用两种类型的框架。受约束的框架和不受约束的。不受限制的框架(在.NET中)使您可以测试和替换所有内容,而无需考虑访问修饰符。即您可以存根和模拟私有和受保护的组件。

我见过的大多数项目都使用受约束的框架(RhinoMocks,NSubstitute,Moq)。使用这些框架进行测试时,必须以一种可以在运行时注入和替换依赖项的方式设计应用程序。这意味着您必须具有松散耦合的设计。松耦合的设计(正确处理时)意味着更好的关注点分离,这是一件好事。

总而言之,我认为背后的想法是,如果您的设计是可测试的,则它是松散耦合的,并且具有良好的关注点分离。

附带一提,我看到了真正可测试的应用程序,但是从面向对象设计的角度看,这些应用程序编写得很差。

0
CodeART