为什么敏捷只涉及测试驱动开发(TDD)而不是开发驱动测试(DDT)?


74

因此,我是敏捷的新手,但不是测试驱动的开发。我在大学里的教授都是关于测试的思想,然后是代码然后是测试。我不确定我理解为什么。从我的角度来看,随着代码的发展,很可能会改变很多前期成本。

这就是我对TDD的想象以及为什么让我感到困惑。如果我要作为TDD承包商来盖房子。

  1. 给我您所有的规格(故事)。

  2. 获得有关规格的批准。

  3. 将所有规格分解为我认为需要的检查(展望未来)。

  4. 打电话给检查员看一下这些要点,然后告诉我,我现在检查不及格(谢谢)。

  5. 开始盖房子。

  6. 每天召回检查员(通过2/100)。

  7. 抱歉,我的理解存在问题,现在我需要再添加9个检查并更改其中的27个。

  8. 呼叫检查员通过1/109。

  9. 该死的。检查员为什么不喜欢这个...哦,我更新了该方法名称...

  10. 建立更多。

  11. UGGGGHHHH更多更改让我更新了该死的检查器。哦,我没有失败。

  12. 我做完了吗

好的,那可能有些古怪,但是直到我的代码在那里,我才看不到如何知道所有方法以及事情将如何工作。99%的时间我必须返回并以任何方式更新单元测试,并在添加时添加更多内容。似乎倒退了。

似乎更合适的是DDT或开发驱动的测试,这似乎是社区几乎忘却的事情。

据我了解,房屋的滴滴涕看起来像:

  1. 给我您所有的规格(故事)。

  2. 获得有关规格的批准并将其发布。

  3. 启动一个单元(基础)。

  4. 记下一些棘手的逻辑(注释)。

  5. 在开始下一个单元的最后,进行检查(创建测试)。

  6. 解决发现的所有问题,然后再次检查。

  7. 批准将本单元移至下一个。

如果我们都说实话,这听起来不是更人性化,而是集中在开发人员和业务上吗?似乎可以更快地进行更改,而且似乎不会产生开销的TDD。


60
对我而言,明显的断开连接的想法是,在编写开始通过测试的代码之前,您可以进行100多个失败的测试。正如@Telastyn所说,一次最多只能有几个失败的测试。“先测试,然后代码,然后测试”并不意味着在编写任何代码之前先编写应用程序的所有测试。
埃里克·金

33
隐喻是一种反模式!人们将花费更多的时间来解释为什么您的隐喻不适合该主题,而不是解决您的实际担忧。在这种情况下,您真正​​关心的似乎是您认为必须预先编写所有测试。
JacquesB

21
作为此处的旁注,TDD通常用于敏捷项目中,但我认为区分敏捷项目管理和敏捷开发实践非常重要。两者之间没有非常紧密的关系。无论项目方法论如何,您都可以使用大多数敏捷开发实践,反之亦然。人们常常忘记了,敏捷本质上是真正地根据需要调整方法以达到最佳结果,而不是必须执行的规定性清单。换句话说,敏捷!= Scrum + TDD。
JimmyJames

35
根据记录,将软件开发与建造房屋进行比较是毫无意义的。研究已经完成。结果出来了。答案很明确。您根本不会像建造房屋那样建造软件。的。二。是。不同。
riwalk

14
敏捷是所有关于快速反馈回路,TDD只是一个反馈回路的特殊类型
JK。

Answers:


83

仅当您还进行紧急设计时,才会实现TDD方法的优势之一。

因此,在您的第一个类比中,您不会编写100个测试,因为您不可能知道软件的外观。

您编写一个测试。您运行它。它失败。您编写最小的代码单元以通过测试。然后,您再次运行测试。它通过了。

现在编写下一个测试,重复以上过程。

当很明显您的代码打算做什么时,这在开始时似乎是一种浪费的方法,但是这种方法的好处是您的测试覆盖率始终很高,并且这种方式的代码设计更加简洁。

作为一种方法,它与结对编程紧密结合。一对编写测试,第二对编写代码以使其通过,然后编写下一个测试,依此类推。

在编写新类时,我什至使用这种方法。第一个测试是调用以启动类构造函数。但是我还没有写好这门课,所以失败了。接下来,我编写简单的空类,并且我的第一个测试通过。

一旦您进入了思维定式,就很难陷入其中,并以“老式”的方式编写代码。

我建议在一个良好的敏捷环境中工作以学习此知识,或阅读一些优秀的敏捷书籍(Clean Code和Clean Coder都很好)以更好地理解。


46
术语“紧急设计”听起来像可以通过测试通过来扩展软件设计。你不能
罗伯特·哈维

28
@nerdlyist:不过,不要忘记设计。您仍然需要设计软件。好的设计不仅会自然地从红绿重构中产生。TDD鼓励良好的设计,但并不是创造出来的。
罗伯特·哈维

19
@RobertHarvey,我完全不同意您的“ “紧急设计”一词,听起来您可以通过测试通过来发展软件设计。您不能 ”这样说。在很多情况下做到了这一点,我可以向您保证。
大卫·阿诺

12
@DavidArno:您是否首先提出了需求规范?您如何将该规范转换为明智的体系结构?您是否每次都通过测试来重新发明它,或者您是否牢记以前的架构原则?没有人会盲目的这样做;首先编写测试没有什么神奇之处,除了它会迫使您使代码更具可测试性。
罗伯特·哈维,

45
@DavidArno我认为您的反对是由于没有给自己作为经验丰富的开发人员/建筑师足够的信誉而引起的。因为您已经知道X模块的直观外观,所以您的测试将朝着正确的方向推动您的开发。一个没有经验的开发人员,编写测试之前很少甚至没有直观的模块应该是什么样的概念,就会构建一个经过良好测试的糟糕模块。
svidgen '16

86

软件不是房子。直觉是好的,但要了解它并不总是正确的。

将所有规格分解为我认为需要的检查(展望未来)。

这是不正确的。在TDD中,您正在描述如何使用代码。规格说:“必须有一所房子,并可以进入它。” 然后测试说:“嘿,我想有一个带旋钮的前门。” 从实现门框,旋钮,钥匙锁等的实现开始(或者争论不休),这远不及将来。

每天召回检查员(通过2/100)。

这是不对的。您不是在为房屋编写测试。您正在为前门框编写测试,然后使其变为绿色。然后测试门是否牢固,使其呈绿色。在任何给定时间,您最多应该可以进行十几个测试。通常,它接近于2到4。

同样,“召集检查员”比喻意味着该人出来并完成工作需要花费一些时间。实际上,为TDD迭代运行单元测试应该花费几秒钟。

似乎可以更快地进行更改,而且似乎不会产生开销的TDD。

开销比您似乎暗示的要少。几秒钟来运行测试可能需要五六次,这与整个开发时间无关紧要。

开发人员首先遇到的问题是,有时当您开始测试时,就会发现存在很大的问题。就像您将床放在马桶旁边,没人愿意在那里生活。修复所需时间比任何类型的TDD开销都要长。由于TDD迫使您从一开始就使用您的代码并考虑如何与之交互,因此TDD可能会遇到一些麻烦。

我在大学里的教授都是关于测试,然后是代码,然后是测试的概念。

话虽这么说,TDD并不是那么普遍。许多地方仍然优先开发,TDD的许多所谓优势已被夸大了。重要的是您要进行测试并使它们良好。您执行工作的顺序不太重要。


18
+1仅用于最后一段。您的房屋示例确实突显了“ religiousTDD”多么愚蠢。
罗伯特·哈维

23
“重要的是您进行测试并使其良好。执行工作的顺序并不那么重要。 ”我是测试驱动开发(TDD)的忠实拥护者,并经常为此提倡。但是,编写代码很麻烦。对于更困难,定义不太明确的问题,通常我必须先编写一些代码,然后才能理解要求。然后,我可以编写需求,然后编写测试。
特雷弗·博伊德·史密斯

1
@TrevorBoydSmith那时不是要编写文档,不是要求。
nerdlyist,2016年

8
因为TDD 从代码用户的角度出发,所以[在最佳情况下]它促进了更好的接口。例如,当我打电话时,open_door我不必传递一个标志toilet_flushed;因此我不会以这种方式编写测试,因此我的代码没有这些“粗糙的边缘”。完全在没有充分考虑如何调用的情况下编写的代码通常具有怪异的界面或假设/前提条件。
brian_o

3
@TrevorBoydSmith给它起个名字,我听说它叫做“ Spike&Stabilize”,是的,当发现如何做您不知道如何做的事情时,这是一种方便的技术。
橡皮鸭

13

构建物理事物和编写软件之间的相似性很小。

也就是说,有一个巨大的区别值得指出:

“编写测试”和“执行测试”之间是有区别的。

在建造房屋的示例中,要求和测试确实在实际建造之前。测试套件的各个部分甚至可以由多个代理连续运行!当建筑商拿起2x4时,他立即根据他的“ 2x4听起来像什么”的概念自动“测试”该单元。提起需求后,他没有编写需求。他对此进行了预先检查。

同样对于组装好的墙壁,电箱,管道等-已经存在测试/要求; 他们在工作中每个人的训练有素的眼中隐含地自动运行不断。当检查员访问时,将执行另一套预先存在的测试。如果这些单元的建造没有通过那些先前存在的测试,则它们将失败。

对于整个结构也同样如此。计划已预先存在。在此过程的每一步中,工程师都在朝着预先存在的一组条件工作,这些条件将在扩建完成后最终针对该结构进行测试

就是说,并非每个物理项目都必须先进行大量测试。如果您去工作坊并开始将木头放在一起作为玩具箱之类的东西,让您的直觉和创造力引导您,那不是严格的TDD木工。在那种情况下,您基本上是依靠介质的物理定律和您对工作的粗略期望(即,“如果它可以编译并且可以工作,那就太好了!”)。

没关系。并非所有事情都需要经过严格的测试。

tl; dr

即使construction!=编写软件:Construction确实以测试驱动的方式运行。对于每个单元的“传球”的条件在其建立之前。

不要将“正在执行的测试”与“编写测试”混为一谈。

并非所有内容都需要是TDD。


我得到代码!=物理类似。但这实际上为我清除了更多。正如我的其他评论所指出的那样,我想知道是否首先创建测试会阻碍现在返回并进行较大更改的愿望?
nerdlyist,2016年

@nerdlyist在某种程度上,是的。但是,如果您的测试正在检查“正确”的事情,那么这就是您想要的“阻碍” 。
svidgen '16

4
回到房屋类比:如果您的客户突然想换用另一种风格的门,而门的尺寸不一样,则对门和开门尺寸的要求将发生变化,并且工作需要更改这些“测试” ”应反映进行实施更改所需的工作。但是,有很多测试不会改变:门必须打开,关闭,锁定,并且在关闭时必须在开口处保持P%的气密性,等等。很多测试不会改变“阻碍” 适当改变身体。
svidgen '16

1
如果我可以再次+1,请考虑一下最后一点。并非所有内容都需要测试。另外,重新阅读它使我觉得也许我正在将TDD单元测试与集成和回归测试相融合。工人进行自动测试是检查员进行集成的单位,进行较大更改时将进行回归测试。
nerdlyist

1
@nerdlyist集成和回归测试也可以首先进行测试。这样做并不完全适合“ TDD教条主义者”的开发周期,但是结果大致相同:您最终得到了可测试的代码,最少的无关代码,并且进行了实际验证其要求的测试。编写代码后进行测试可能会使完成这些事情中的任何一件或全部困难一些。并非不可能,但可能会更加困难。
svidgen '16

11

您已经陷入陷阱,相信胡说八道,即编写软件类似于盖房。不是。这更类似于创建建筑师图纸和结构工程师计算。

现在有了真正的房子,建筑师会预先创建这些计划。然后,您召集建造者,他们开始建造,遇到问题,必须进行修改,获得建筑控制权,谁想要进行更改等。最后,建筑师返回并向您收费,以更新他的图纸以反映发生了什么。这是一种cr脚的做事方式,但是房屋建造时间长且价格昂贵,因此这是唯一实用的方式。

对于软件工程而言,情况会更好。相当于建造房屋的过程是让编译器将您的代码转换为某种已编译的单元(库,应用程序,Web应用程序等)。每天执行数百次此操作非常快速且便宜。结果,在添加功能时重复重建房屋毫无意义,仅在最后调用检查器(QA)进行测试。相反,如果自动执行这些检查,则可以让检查器在每次重建时都重新检查所有内容。

无论您遵循严格的TDD,还是采用面向测试的方法编写一些代码,然后进行一些测试,都没有关系。我更喜欢第一种方法,而后一种则更好。选择一个适合您的。重要的是,您可以在进行过程中创建这些检查。当您想更改代码时,它们会在以后通过警告您更改其他地方的功能中断来提供帮助。


1
我看到您当时已经处理过建筑控制... :)
Jules

5
“这是一种cr脚的工作方式,但是房屋要花很长时间才能建造,而且价格昂贵,所以这是唯一可行的方式。”:确实,在软件中您可以非常快速地重建,但是如果您犯了一个完全错误的话。一开始的决定,以后可能需要大量重写。渐进式设计并不总是会遇到此类问题:您需要一步步移动,直到发现自己陷入死胡同,然后您必须返回。没有银弹。
Giorgio'8

2
@Giorgio-发生大量重写是因为单个决策反映在代码中的许多地方。当决定改变时,代码将在很多地方改变。连续重构通过消除重复并限制反映单个决策的代码量来缓解此问题。良好的测试覆盖范围支持连续重构。
凯文·克莱恩

7

首先,最重要的是前期成本没有您想象的那么高。是的,与不进行测试相比,您花费更多的时间进行测试。但是,如果您采用“测试后”方法,那么您真正浪费的是什么?假设TDD需要10个小时,而DDT则需要6个小时。您的“额外”前期费用仅为4小时。现在,如果您应用代码度量标准或要求(例如90%的覆盖率),则TDD和DDT的成本将更加接近。

您将使用TDD编写更少的错误代码。即使只是因为您已将需求明确说明为测试,也可以在一天结束时证明您的代码完全按照您想要的去做。现在也许您想让它做错事,但这不是一个变更请求的错误。这个很重要。“销售”有效的产品比较容易,但是可以以不同的方式/更好地工作,然后出售被认为无效的产品。使用TDD,实际上不可能通过测试并编写不起作用的代码。您可能不理解这些要求,并且编写了错误但有效的代码。

代码库越旧,TDD越好。尝试在没有测试套件或实施不良的套件的情况下进行重构。即使是简单的更改也会引入错误。拥有覆盖面广的测试套件可确保随着产品的发展,它继续按预期运行。它还有助于突出显示冲突的业务规则(在较长时期内发生)。

您不知道它不起作用。如果没有测试套件,您将无法确定代码是否按照您认为的方式运行,或者看起来是否正常。

var foo = function(in) {
    if(in == 0) {
      return true
    }
}

现在,在您的整个应用程序中,您都会调用if(foo()){ doStuff() }修复foo时会发生什么?

var foo = function(in) {
    if(in === 0) {
      return true
    }
}

您也应该重构和干燥测试。一个好的测试套件并不难维护。有了编写良好的原子测试,我几乎不必回过头去一次更改超过1-2个测试。如果对测试套件进行了较大的更改,这是一个巨大的危险信号,那就是某些事情不正确。

我只是看不到我应该如何知道我的所有方法以及事情将如何工作,直到我的代码出现为止。

好吧,你不应该这样。您应该编写一个测试来测试某个工作单元是否完成。如果您觉得自己正在测试一些您可能不知道的事情,那么您就在考虑太大,或者测试太小。

例如,您需要知道门已关闭并锁上。我将测试door.close()和door.lock(),当门被锁定时,door.open()返回false。这就对了。如果您的测试是door.lock(),则在数据库中设置一个标志。然后您测试得太小。该测试不需要知道door.lock()是如何工作的,只需要知道。

现在,如果您正在编写一个测试,说所有门窗都被锁定,house.isSecure()将返回true。如果不先看门或窗,那么您的想法就太大了。

最后,您可能正在考虑太大的工作单元。当您获得需求列表时,应该对它们进行整理,以便在最小的单元上工作。仅针对该单元编写测试,然后编写代码,然后冲洗并重复。

从本质上讲,您对TDD应该如何工作的理解(清单)已经关闭。您永远不会传2/100。您应该先通过1/1,再通过2/2,再通过3/3,再通过4/4,依此类推。

为您修改的清单

  1. 获取所有规格
  2. 选择一个规格
  3. 测试一下
  4. 编码
  5. 测试一下
  6. 如果测试通过,则转到7,否则转到4
  7. 如果已完成所有规格,则转到8,否则转到2
  8. 与使用者一起检查规格,并在需要时添加新规格。如果操作正确,则完全不必更改任何测试。只需添加新的。有时需求收集可能会崩溃,您必须添加与早期测试冲突的测试,但是您几乎不必更改测试。

2
我喜欢这个答案,因为它突出了TDD的真正优势-使旧代码库易于管理。许多只从事项目未开发阶段工作的人没有看到TDD的好处,因为它看起来像是额外的工作。当您必须升级和维护现有代码库时,这确实会有所回报。我希望他们在学校能给您一个项目,然后在您开始工作后就要求进行一系列功能更改。这将更真实地反映现实并证明TDD的价值。
thomij '16

1
我也喜欢这个答案。请注意,我们往往会忘记实际花费多少时间来手动执行测试:编辑,编译,逐步进行手动调试,重复。TDD可以自动执行大部分操作。
Technophile

在这里让我大吃一惊的是,需要注意的是,当您在TDD中编写单元测试时,您并不是在为某个客户端功能规范编写测试,而是在编写针对客户端功能规范的测试。当您(一个程序员)在您的大脑中定义了单位时,即“当X发生时,该类应该引发一个事件,让我们为此做一个测试”。这个含糊的“您的测试是您的规格!” 当您已经获得TDD时,修辞是很好的选择,但对于跳出来的人,它提出的问题多于答案。
Ant P

4

我觉得有些键缺少其他答案。

三大优势

首先,错误的成本和变更的成本在软件和房屋之间的差异是如此之大,以致于某些规则可能不适用。例如,测试物理结构的成本如此之高,以至于您需要对其运行有高度的信心,以免浪费测试。使用软件,如果您可以在1-5秒内运行一套单元测试,那么我们可以提供不同的选择。

其次,在编写被测代码之前执行您期望失败的测试的目的是验证测试本身。当然,这看起来很愚蠢或浪费。但是我已经看到足够多的单元测试以始终通过的方式编写,即使被测代码已损坏也是如此。在编写被测代码,手动进行测试然后编写单元测试时,很容易发生这种情况。如果您编写一个单元测试,看到它失败了,那么当您编写使它通过并通过的代码时,您就知道您的测试是正确的。如果被测代码退步,则您的单元测试很有可能抓住它。

TDD的第三个优点(通常不提及)是所产生的代码大小和复杂度通常要小一个数量级。我一直以自己喜欢简单简洁的代码而感到自豪。直到我开始练习TDD。TDD向我展示了我之前会做的事情是过多的代码。为什么会这样?因为您编写了测试,所以请编写代码以使测试通过。测试通过后,就可以完成。如果您采用这种思维方式,则很难意外地编写“额外”代码。

(在我以前使用的产品中,我已经观察到了额外的代码问题。严重的问题来自于没有人要求的代码,但是一些开发人员认为这很酷。)

成本分析

因此,从某些方面来说,您是对的。建房时我们无法摆脱这种策略。太贵了。但是软件不是房子。软件便宜。

与房屋类比是组装房屋与软件编译器的工作。

在非TDD的世界中,开发人员仍然会迭代。我们遵循一个代码->编译->运行->测试周期。我们已经处于一个与建造房屋大不相同的模型中。如果您的施工人员先构建门框,然后构建门,然后由于门太大而不得不重新构建框架,那么您将面临成本问题。因此,您需要花费更多的时间进行前期准备,以确保一切都变得完美。在编程中,大多数项目都可以在几秒钟或几分钟内完成编译,因此,如果某件事不尽早就没关系了。提前考虑琐碎问题的成本通常超过重新编译的成本。因此,我们一直在重新编译。

TDD是相同的原理,只是旋转即可进行测试。如果可以使测试变得非常便宜(因此所有测试只需几秒钟即可完成),那么在一次大爆炸式编码迭代中仔细考虑全局,完美,整体解决方案的成本将超过重构的成本。

除了...

在编程中有些事情这些论据不成立。那就是建筑的目的。确定并计划前期顾虑,这些事后更改将更加昂贵。例如,您不会在不考虑自己的需求,开始构建而只是争辩说以后可以更改它的情况下即时选择数据库。您需要仔细考虑。


1
关于验证测试的重点!另外:建造房屋时,很难移动墙壁(这会留下疤痕)。借助软件,移动代码相对容易-只要您具有自动测试功能即可捕获任何意外更改。
Technophile

4

在我做过一些TDD项目之前,我常常对此感到好奇。在执行此操作时,出现了一个非常简洁而全面的解释:

在编写代码时,您必须采用某种方法来确保代码具有有意义的作用。因此,您可以测试代码。您可以进行临时测试。但是,如果在开始做事之前考虑如何进行测试,则测试效果会更好。因此,在进行测试之前,请先设计测试策略。

现在,既然您正在考虑自己的测试策略,那么您可能至少会自动化其中的一部分...而且您知道您拥有一定程度的TDD。在通过测试之前编写代码的事实很正常。无论如何,这就是您要做的,编写代码直到工作。现在就可以轻松进行测试了。

由于超出范围的原因,您不会一口气写下整个内容。因此,您也不用一口气设计测试。但是基本上就是这样。只是更好的测试计划,您不一定总是预先编写测试。有时,您会随着进度的增加而添加更多内容,并发现意想不到的错误,或者使测试以后变得更强大。有时您可能会发现自己没有设计测试,但是发现手工测试很麻烦,因此您以后可以进行测试。

因此,TDD只是查看您如何验证工作的一种极端方法。


4

尽管这个问题已经有了一个可以接受的答案,但我相信我还有一点要补充,它来自于无书面测试的设计风格(所有测试由“测试人员”按照测试程序手动执行)到TDD。这些只是我个人的观察,尽管我认为它们相当普遍。

当您编写新内容之前从未做过的事情时,TDD与不执行TDD之间没有显着差异。

通常,您要做的是编写一小段代码来探索一个想法,然后添加一些硬编码的位以进行“测试”,然后编译和/或执行它。如果可行,您将删除硬编码的内容,并通过添加参数,实例变量等来泛化代码。

想一想。这与TDD的工作量完全相同。唯一的区别是TDD中,您将“测试”位分别写入另一个文件中,而不是最后将其删除。在TDD中,您保留测试代码

当然,TDD的组织性稍强,这意味着需要做更多的工作来弄清楚如何将代码的测试位与实际代码分开。但是,如果您已经编写了单元测试,那么您将学习如何模块化代码进行测试。


2

为什么敏捷只涉及测试驱动开发(TDD)而不是开发驱动测试(DDT)?

只是在这里大声疾呼,是因为我发现这个问题暗示了一个事实(“敏捷全都涉及TDD”),我觉得这是令人反感的。所有答案似乎都认为这一事实是理所当然的。如果您认为敏捷性主要与TDD有关(也就是在单元级别进行测试),那么它们是很好的答案。

https://zh.wikipedia.org/wiki/Agile_software_development#Agile_methods列出了许多或更多不同的开发模型。

我会特别提供行为驱动开发和功能驱动开发作为值得思考的东西。完全不同的野兽可以带来基本TDD的所有好处,但与简单的红绿重构周期相去甚远。

所以我的回答是:

  • “ DDT”(也就是在实现之后或在实现“之上”编写测试)不适用于现实生活中的原因;一旦您有时间或金钱压力,测试就不会进行;无论如何,IMO只是为了测试的好处而进行测试。
  • 如果您将该术语解释为基本上意味着“所有构建基块/类的单元测试”(至少是Wikipedia就是这种情况),那么敏捷就不仅仅涉及测试驱动开发(TDD)。TDD只是进行敏捷开发的一种可能性。
  • 还有其他方法,例如BDD或FDD,可以采用全栈方法。您仍然需要预先编写方案,仍然有一个红绿色重构周期,并且仍然只能实施,直到方案变为绿色为止,但是根据定义,您的“测试”可以使整个软件(通过像用户交互一样工作)行使,只有一个单位。

我宁愿拥有一个具有完整的BDD / FDD覆盖范围并且没有单元测试的应用程序,而不是一个具有完整的单元测试覆盖范围并且没有全栈测试的应用程序。

(TDD当然有其位置,例如在API中,但这不是我们在这里谈论的。)

同样,我并不想在这里低调所有其他答案,只是指出这个问题的表达范围很狭窄,并且该领域还有很多事情可以提供。


我知道其他人,但在撰写本文时,这似乎更有意义。要详细说明为什么TDD对于API更好?说带有MVC的Web客户端如何使用它会有所不同?
nerdlyist

1
在API中遵循非常详细的合同非常重要; 对于每个用户(其他程序)而言,每个方法调用的行为都必须非常可预测,才能正确使用它。这是单元测试真正发挥作用的地方。OTOH在应用程序中,对于人类用户需要按预期工作的功能而言,这是最重要的。当然,您的“内部人员”也应该正确无误,但更重要的是要有一个全面的“端到端”功能(即,从UI到数据库再到DB的背面),而不是测试每个单独的方法(最终用户还是看不到)。
AnoE

当然,这里的程度各不相同-如果您在某处应该有非常复杂的方法/类(例如:计算地理坐标之间的距离),那么在这里进行一些单元测试也是有意义的。但是大部分(例如,在业务应用程序中)是整个工作流程。
AnoE

0

尽管您不经常看到它是用这种方式编写的,但敏捷背后的许多理由是,重新编写代码要比第一次编写好。每次重新编写代码时,您都会改进代码并提高自己。第一次“正确”获得它往往会更慢且更脆弱。

房屋类比并没有那么糟糕,但是您必须考虑我们对房屋建造了解了多长时间与我们对软件工程了解了多久,而且软件工程的复杂性更接近于建造一座漫长的桥梁或比房子高20层的塔楼。我们还应该考虑到,通过构建新的语言和工具,就​​像每个建筑商都想要用完全不同的形状,大小和材料来建造建筑物。完全混乱。

但是,检查不是与代码测试很好的类比。在软件方面,我们甚至还没有像样的检查员(您的各种技能水平的同行除外)。除了一些可能是任意的“编码标准”(我们更像测试油漆的颜色和草坪的布局而不是结构)之外,我们也没有任何法规需要检查。

测试优先更像是建造一堵墙,然后对它施加一些压力以确保稳定性,然后再将其举起并放置在您的房屋上。这就像测量窗口,以确保将其放入您要放置的孔之前。

如果您不得不在没有数百年以前的架构知识,模式,法规和数学知识的情况下建造一系列桥梁,而在构建桥梁之前,我们必须证明我们的桥梁能够正常工作,那么您可能需要对桥梁进行大量测试和重建。

最后,这是一个非类比点-使用软件,每次允许您重新编写代码的一部分时,都可以显着提高代码和技能。抓住一切机会,重新编写代码。TDD可能是一个很好的借口。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.