唯一的开发人员应使用TDD的原因有哪些?
我是一个有很多经验的合同程序员。我习惯于被客户聘请去从事我自己的一种或者另一种形式的软件项目,通常一无是处。这意味着几乎每次都清理一块石板。我可以引入为快速入门而开发的库,但是它们始终是可选的。 (并取决于是否在合同中获得正确的IP条款)很多时候,我可以指定甚至设计硬件平台...因此,我们在这里谈论的是严重的自由。
我可以看到用于构造某些代码的自动化测试的用途:具有不仅仅琐碎功能的库,具有大量引用的核心功能等。基本上,随着一段代码的价值随着大量使用而上升,我可以看到它自动测试该代码将变得越来越有价值,这样我就知道我不会破坏它。
但是,就我的情况而言,我发现除此以外的其他事情都很难合理化。我会采纳那些被证明有用的东西,但是我不会盲目地遵循任何东西。
我发现我在"维护"中所做的许多事情实际上都是很小的设计更改。在这种情况下,测试不会为我节省任何费用,现在它们也必须进行更改。高度迭代的,基于存根的设计方法对我来说非常有效。我看不到通过进行更广泛的测试实际上能为自己节省很多时间。
业余项目甚至更难以证明……从周末到一个月的时间里,它们通常是任何东西。边缘错误几乎无关紧要,这全都与玩某件事有关。
阅读诸如此类的问题,投票最多的回答似乎是说,在海报的经验/观点中,TDD实际上浪费时间,如果人数少于5人(即使假设具有一定水平的TDD能力/经验)。但是,这似乎涵盖了最初的开发时间,而不是维护时间。尚不清楚TDD在项目的整个生命周期中如何堆积。
我认为TDD可能是朝着提高我们整个行业的产品质量这一有价值目标迈出的重要一步。但是,唯心主义本身并不能有效地激励我。
我确实认为TDD在大型团队或者至少包含一个不可靠程序员的任何规模的团队中都是一个很好的方法。那不是我的问题。
为什么拥有良好业绩的唯一开发商会采用TDD?
我很想听听在TDD上完成的任何形式的指标(正式或者不正式)...侧重于单独的开发人员或者非常小的团队。
否则,个人经历的轶事也将是一件很不错的事情。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 :)
请避免陈述没有经验的观点来支持它。让我们不要将其打造成意识形态战争。同样,跳过更大的就业选择的论点。这只是效率问题。
解决方案
当我独自飞行时,我发现它甚至更有用。由于没有人可以跳出想法,也没有人可以进行同行评审,因此我们将需要确保自己的代码是可靠的。 TDD / BDD将为我们提供保证。但是,TDD有点矛盾。其他人可能完全不同意我的意思。
编辑:可能我补充道,如果做对了,我们实际上可以在编写测试的同时为软件生成规范。这是BDD的巨大副作用。如果我们要自己编写可靠的代码和规范,则可以使自己看起来像超级开发人员。
我们是否是唯一的开发人员都没有关系。我们必须从应用程序的角度考虑它。所有的应用程序都需要正常工作,所有的应用程序都需要维护,所有的应用程序都需要减少错误。当然,在某些情况下,TDD方法可能不适合我们。这是截止日期非常快且没有时间执行单元测试的时候。
无论如何,TDD并不依赖于个人或者团队环境。这取决于整个应用程序。
我没有大量的经验,但是我有看到截然不同的测试方法的经验。
在一项工作中,没有自动化测试。 "测试"包括在应用程序中四处逛逛,尝试一下脑海中弹出的内容,看看它是否坏了。不用说,断断续续的代码很容易到达我们的生产服务器。
在我目前的工作中,有很多自动化测试和完整的CI系统。现在,当代码被破坏时,立即显而易见。不仅如此,而且在我工作时,测试确实记录了我的代码中哪些功能在起作用,哪些还没有。知道如果我破坏了现有功能,它将为我带来极大的信心,使其能够添加新功能。
因此,对我而言,这不取决于团队的规模,而是取决于应用程序的规模。我们可以跟踪应用程序的每个部分吗?每个需求?我们需要运行以确保应用程序正常运行的每个测试吗?如果我们没有测试来证明应用程序正在运行,那什至意味着该应用程序正在"运行"呢?
就是我的$ 0.02.
我将很快回答这个问题,希望即使我们仍然不同意,我们也将开始了解其中的一些原因。 :)
如果我们有幸参加了一个长期运行的项目,那么有时候,例如,我们需要先编写数据层,然后再编写业务层,然后再进行升级。如果客户随后进行了要求更改,需要对数据层进行重新处理,则数据层上的一组单元测试将确保方法不会以不良的方式失败(假设我们更新测试以反映新的要求) )。但是,我们也可能会在业务层中甚至在多个地方调用数据层方法。
假设我们在业务层中有3次对某个方法的调用,但是我们只修改了2. 在第三个方法中,我们仍然可能会从看起来有效的数据层取回数据,但是可能会破坏某些假设几个月前编码的。在此级别(及更高级别)上的单元测试应该被设计为可以发现破损的假设,如果失败,它们应该向我们突出表明有一部分代码需要重新审视。
我希望这个非常简单的示例足以使我们对TDD有所了解,并且它可能会产生火花,使我们考虑使用它。当然,如果我们仍然不明白这一点,并且我们对跟踪成千上万行代码的能力充满信心,那么我无处告诉我们应该启动TDD。
I'm not about to blindly follow anything.
那是正确的态度。我一直都在使用TDD,但是我并没有严格遵守它。
支持TDD的最佳论据(在我看来)是,当我们最终进入项目的重构和维护阶段时,我们将获得一组可以运行的测试。如果这是使用TDD的唯一原因,那么我们可以随时编写测试,而不必盲目地遵循该方法。
我使用TDD的另一个原因是编写测试让我提前考虑了我的API。在编写它之前,我不得不考虑如何使用一个类。在这个高水平上投入我的项目对我来说是有效的。还有其他方法可以执行此操作,如果我们发现其他方法(有很多方法)可以执行相同的操作,那么我想说的是继续做对我们有用的事情。
TDD与测试无关,而是与编写代码有关。这样,它甚至为单个开发人员也提供了很多好处。对于许多开发人员而言,编写更强大的代码是一种思想转变。例如,我们多久想一次"现在该代码怎么会失败?"编写没有TDD的代码后?对于许多开发人员来说,这个问题的答案是没有。对于TDD实践者,它将思想转变为执行诸如在对象或者字符串为空之前检查对象或者字符串是否为空的事情,这是因为我们正在编写专门用于执行此操作的测试(破坏代码)。
另一个主要原因是变化。每当我们与客户打交道时,他们似乎永远无法下定决心。唯一不变的是变化。 TDD可以作为"安全网"来查找所有其他可能破坏的区域。即使在小型项目中,这也可以使我们避免在调试器中浪费宝贵的时间。
我可以继续下去,但我想说的是,TDD不仅仅是编写代码,还不应该证明它是唯一的开发人员。
首先编写测试的要点是,它可以执行我们所制定的要求和设计决策。修改代码时,我想确保它们仍然得到执行,并且很容易"破坏"某些东西而不会出现编译器或者运行时错误。
我有一个测试优先的方法,因为我想对自己的代码有高度的信心。当然,测试必须是良好的测试,否则它们不会执行任何操作。
我有一些相当大的代码库可以使用,并且有很多不平凡的事情在进行。进行更改很容易引起涟漪,当X永远不应该发生时,突然发生X发生。我的测试多次使我免于犯下可能被人工测试人员忽略的严重(但微妙)错误。
当测试确实失败时,他们就有机会查看它们和生产代码,并确保它是正确的。有时,设计会发生变化,并且测试需要进行修改。有时候我会写一些东西通过100个测试中的99个。那个没有通过的测试就像一个同事在某种程度上检查我的代码,以确保我仍在构建应该构建的代码。
我倾向于同意我们关于"一个开发人员"或者"爱好"项目的TDD开销的观点的合理性,而不证明费用是合理的。
但是,我们必须考虑到,如果长时间持续使用它们,那么大多数最佳实践都是相关且有用的。
例如,TDD可以长期节省测试/错误修复时间,而不是在我们创建第一个单元测试后的5分钟内。
我们是一名合同程序员,这意味着我们将在完成当前项目后离开该项目,并将切换到其他项目,很可能在另一家公司。我们当前的客户将必须维护和支持应用程序。如果我们不离开支持团队,那么与他们一起工作的良好框架将陷于困境。 TDD该项目实现可持续发展。它将增加代码库的稳定性,因此经验不足的其他人将无法对更改代码造成太大的损害。
爱好项目也是如此。我们可能会对此感到厌倦,并希望将其传递给某人。我们可能会在商业上取得成功(想想Craiglist),除了我们之外,还会有5个人在工作。
即使只是获得经验,对适当过程的投资也总能带来回报。但是大多数时候,我们会很感激当我们开始一个新项目时,我们决定正确地执行它
做某事时,我们必须考虑其他人。我们必须提前考虑,规划增长,规划可持续性。
如果我们不想那样做就坚持牛仔编码,用这种方法会简单得多。
P.S.同样的情况也适用于其他实践:
- 如果我们不注释代码,并且具有理想的内存,那么我们会没事的,但是其他人则不会。
- 如果我们不记录与客户的讨论,其他人将对我们做出的关键决定一无所知
无限等
通过测试,我们可以放心重构,而不会破坏系统。首先编写测试可以使测试定义系统的正常工作行为。根据定义,测试未定义的任何行为都是副产品,并且在重构时可以更改。首先编写测试也可以朝着正确的方向推动设计。为了支持可测试性,我们发现需要解耦类,使用接口并遵循良好的模式(例如,控制反转),以使代码易于测试。如果事后编写测试,则不能确保已涵盖测试中系统预期的所有行为。我们还会发现,由于设计的原因,有些东西很难测试-因为它可能是在开发时没有考虑到测试的原因-并倾向于跳过或者忽略测试。
我通常都是独自工作,大部分时间都在TDD上工作-在这种情况下,我不是仅仅不能遵守自己的做法,或者还没有找到适合我进行TDD的好方法,例如使用Web界面。
如果没有一套合理的单元测试,我将不再重构任何东西。
我不会先对单元测试进行全功能TDD,然后对代码进行完整的测试。我执行CALTAL-代码A小,测试少许-开发。通常,代码优先,但并非总是如此。
当我发现需要重构时,请确保已经进行了足够的测试,然后我完全自信地破坏了该结构,而不必保留整个旧体系结构成为新体系结构计划在我的脑海。我只需要让测试再次通过即可。
我重构了重要的部分。使现有的测试套件通过。
然后我意识到我忘记了一些东西,然后我回到新事物的CALTAL开发中。
然后我看到我忘记删除的东西-但是它们真的到处都没有用吗?删除'em,然后查看测试失败。
就在昨天-经过大的重构的一部分-我意识到我仍然没有正确的设计。但是测试仍然必须通过,因此我可以在进行第一次重构之前自由地重构自己的重构。 (哇!)这一切都很好,因为我进行了一组测试以验证更改的依据。
对于单飞,TDD是我的副驾驶。
我觉得作为一个项目(尤其是一个较大的项目)的单独开发人员,我们倾向于分散地工作。
我们正处于大型重构的中间,突然发现了一些由于某些原因在预发布测试中未出现的严重错误。在这种情况下,我们必须丢下所有东西并修复它们,花了两周时间将头发弄干后,我们终于可以恢复以前的工作了。
一周后,最大客户之一意识到他们绝对必须具有这个炫酷的新光泽功能,否则他们将不会下个月订购的100万个单位的订单。
现在,三个月后,我们甚至都不记得为什么首先要进行重构了,更不用说重构代码应该做什么了。谢天谢地,我们在编写这些单元测试方面做得很好,因为至少它们告诉我们重构的代码仍在执行应有的功能。
泡沫,冲洗,重复。
..过去6个月的人生故事。 :-/
唯一的开发人员应在其项目上使用TDD(跟踪记录无关紧要),因为最终该项目可以传递给其他开发人员。或者可以招募更多的开发人员。
如果没有测试,新手将很难处理代码。他们会破坏事情。
客户在交付产品时是否拥有源代码?如果我们可以说服他们使用单元测试交付产品可以增加价值,那么我们就可以提升服务质量并交付更好的产品。从客户的角度来看,测试覆盖率不仅可以确保质量,而且由于测试将功能与UI隔离开来,因此将来的维护人员可以更轻松地理解代码。
我对TDD的最佳体验是围绕pyftpdlib项目。大部分开发工作都是由原始作者完成的,我做了一些小的贡献,但这本质上是一个单独的项目。该项目的测试套件非常详尽,并且测试了FTPd库的所有主要功能。在签入更改或者发布版本之前,将检查所有测试,并在添加新功能时也总是更新测试套件。
由于采用了这种方法,因此这是我从事过的唯一项目,在新版本发布后,没有显示topstop错误,检查过更改是否破坏了主要功能,等等。代码非常可靠,我在项目生命周期内打开的错误报告很少,这一直给人留下深刻的印象。我(和原始作者)将这种成功的大部分归功于全面的测试套件以及能够随意测试每个主要代码路径的能力。
从逻辑角度来看,我们编写的任何代码都必须经过测试,并且没有TDD的情况下,我们将自己进行手工测试。在pyftpdlib的另一面,在bug数量和主要问题发生频率方面,最糟糕的代码是仅由开发人员和质量检查人员手动测试的新功能进行测试的代码。由于时间紧迫或者从裂缝中掉下来,所以没有对事物进行测试。遗忘了旧的代码路径,即使最古老的稳定功能也最终被破坏,主要版本最终导致重要功能无法正常工作。手动测试对于验证和测试的随机性至关重要,但是根据我的经验,我想拥有手动测试和精心构建的单元测试框架是必不可少的。两种方法之间的覆盖范围较小,并且我们出现问题的可能性只能降低。
我认为TDD作为一种方法论不仅仅在于"在进行更改时进行测试",因此它不依赖于团队,也不依赖于项目规模。这是关于人们对代码/应用程序要做什么之前开始真正思考所提到的行为是如何实现之前的期望。 TDD的主要重点不仅在于对编写的代码进行测试,而且编写更少的代码,因为我们所做的只是使测试变成绿色的(并在以后进行重构)。
如果我们像我一样,并且发现很难考虑某个部分/整个应用程序的功能而又不考虑如何实现它,那么我认为在代码之后编写测试并让代码"驱动"代码很好测试。
如果问题不是关于测试优先(TDD)还是测试后(良好的编码?)的问题,那么我认为对于任何开发人员(无论是开发团队还是开发团队)来说,测试应该是标准的实践超过三个月。以我的经验,这是一个时间跨度,在此之后甚至连原始作者都必须认真思考这二十行复杂,超级优化但稀疏记录的代码的真正作用。如果我们已经进行了测试(涵盖了代码的所有路径),那么即使是数年之后,也没有什么要思考的,也不必去考虑ERR了。
以下是一些模因和我的回复:
" TDD让我思考如何失败,这使我成为了一个更好的程序员"
如果有足够的经验,那么无论如何都应该自然而然地关注故障模式。
"应用程序需要正常工作"
假设我们能够测试所有内容。与正确地编写功能代码相比,正确地覆盖所有可能的测试要好得多。 "应用程序需要更好地工作"是一个更好的论点。我同意这一点,但它是理想主义的,而且不够实际,无法激发我的期望。指标/轶事在这里会很棒。
"非常适合我的<library component X>"
我说的问题是,在这些情况下,我认为很有价值,但是,感谢轶事。
"思考下一个开发人员"
这可能是我最好的论据之一。但是,下一个开发人员很可能也不会实践TDD,因此在这种情况下将是浪费甚至是负担。后门布道就是它的意义所在。我非常确定TDD开发人员会对此表示赞赏。
当我们继承一个已过时的必须做方法时,我们将欣赏多少? RUP,有人吗?想一想,如果TDD并不像大家所认为的那么伟大,那么TDD对下一个开发人员意味着什么。
"重构要容易得多"
重构是一项与其他技术一样的技能,而迭代式开发无疑需要此技能。如果我认为新的设计从长远来看会节省时间,那么我倾向于扔掉大量的代码,并且感觉好像还会丢弃大量的测试。哪个更有效?我不知道。
...
我可能会向任何新手推荐一定程度的TDD ...但是对于已经来过几次障碍的人来说,我仍然无法获得好处。我可能会开始向库中添加自动测试。这样做之后,我有可能在一般情况下看到更多的价值。
我也是合同程序员。这是我喜欢单元测试的12个理由。
好吧,轮到我了……即使我自己也要进行TDD(对于非秒杀代码/实验代码/原型代码),因为
- 在跃跃欲试之前思考:迫使我在开始编写代码之前思考一下我想完成的工作。我要在这里完成什么呢。"如果我假设我已经拿到了这件作品。我希望它如何工作?"鼓励对象的接口设计。
- 更改更容易:我可以放心地进行修改。"更改了步骤5时,在步骤1-10中没有破坏任何内容。"回归测试是瞬时的
- 出现了更好的设计:我发现没有我在设计活动上投入精力的情况下出现了更好的设计。测试优先+重构导致松散耦合,使用最少方法的最少类..没有过度工程..没有YAGNI代码。这些类具有更好的公共接口,较小的方法并且更具可读性。这是一件禅宗的事情..我们只会在"得到它"时才注意到它。
- 调试器不再是我的拐杖了:我知道我的程序在做什么。如今,如果我在调试器上花费了10分钟以上,则精神警报开始响起。
- 自从TDD以来,我发现我的代码中的bug数量大大减少了,这可以帮助我按时回家。即使断言就像一个控制台跟踪,而不是xUnit类型的AT。
- 生产力/流程:它可以帮助我确定下一个离散的婴儿步骤,它将带我朝完成的方向前进...使雪球不断滚动。 TDD可以帮助我更快地掌握节奏(或者XPers所说的流程)。我在单位时间内完成的质量工作比以前要多得多。红绿重构循环变成了……一种永动机。
- 我可以证明我的代码只要按一下按钮就可以工作
- 练习使我感到完美,我发现自己可以更快地学习和发现龙,这使我有更多的TDD时间。也许不和谐..但是我觉得即使我不先进行测试,TDD也使我成为一个更好的程序员。发现重构机会已成为第二天性。
如果有其他想法,我将进行更新..这是我在反思的最后2分钟想到的。
TDD使我可以更清楚地定义问题。这有助于我专注于仅实现所需的功能,仅此而已。它还可以帮助我创建更好的API,因为在编写代码本身之前,我正在编写"客户端"。我也可以重构而不必担心破坏任何东西。
有动机的自我利益。
就我而言,唯一的开发人员可以转化为小型企业所有者。我编写了相当数量的库代码,(表面上)使我的生活更轻松。这些例程和类中有很多不是火箭科学,因此我可以通过回顾代码,对它们进行一些现场测试和调试以确保它们的行为方式,来确保它们正常工作(至少在大多数情况下)(至少在大多数情况下)我认为他们有。蛮力,如果愿意的话。生活很好。
随着时间的流逝,该库不断增长,并在更多的项目中为不同的客户使用。测试变得更加耗时。特别是在我(希望)修复错误并且(甚至希望)不破坏其他东西的情况下。这不仅是针对我代码中的错误。我必须小心添加功能(客户不断要求更多的"东西"),或者确保在将代码移至新版本的编译器(Delphi!),第三方代码,运行时环境或者操作系统时代码仍然可以正常工作。
极端地说,我花更多的时间在检查旧代码上,而不是在新的项目上阅读。可以将其视为软件的休止角(我们可以将未经测试的软件堆叠到多高:)。
诸如TDD之类的技术为我提供了经过深思熟虑设计,经过了更全面测试(在客户得到之前)且无需进行过多维护的方法和类。
最终,它意味着更少的维护时间和更多的时间花在了更有利可图,更有趣(几乎任何事情)和更重要(例如家庭事务)上的事情上。