为什么一个单独的开发人员应该使用TDD



我是一名有丰富经验的合同程序员。我习惯于被客户雇佣,独自完成一个形式各异的软件项目,通常是白手起家。这意味着几乎每一次都是清白的。我可以引入我开发的库来快速入门,但它们总是可选的。(取决于合同中是否有正确的IP条款)很多时候我可以指定甚至设计硬件平台。。。所以我们在这里谈论的是严肃的自由。

我可以看到为某些代码构建自动测试的用途:具有非琐碎功能的库、具有大量引用的核心功能等。基本上,随着一段代码的价值在大量使用中不断增加,我可以看到自动测试该代码会越来越有价值,这样我就知道我不会破坏它

然而,在我的情况下,我发现很难找到比这更合理的理由。我会采纳有用的东西,但我不会盲目地遵循任何东西。

我发现我在"维护"中做的很多事情实际上都是小的设计更改。在这种情况下,测试不会为我节省任何东西,现在它们也必须改变。一种高度迭代、存根优先的设计方法对我来说效果很好。我看不出用更广泛的测试实际上能节省那么多时间。

业余项目更难证明。。。他们通常是从周末到一个月的任何人。边缘案例错误很少重要,这都是关于玩一些东西。

阅读像这样的问题,投票最多的回答似乎是说,根据发帖者的经验/意见,如果你的人数少于5人,TDD实际上是在浪费时间(即使假设你有一定的TDD能力/经验)。然而,这似乎涵盖了最初的开发时间,而不是维护。目前还不清楚TDD在项目的整个生命周期中是如何累积的。

我认为TDD可能是朝着提高整个行业产品质量这一有价值的目标迈出的一大步。然而,理想主义本身已经不再那么有效地激励我了。

我认为TDD对于大型团队或任何规模的团队来说都是一个很好的方法,其中至少包含一个不可靠的程序员。这不是我的问题。

为什么只有一个有良好业绩记录的开发人员会采用TDD

我很想听到在TDD上做任何类型的度量(正式或不正式)。。。专注于单独的开发人员或非常小的团队。

如果做不到这一点,你个人经历的轶事也会很好。:)

请避免在没有经验的情况下发表意见。我们不要把这当成一场意识形态战争。还有跳过更多就业选择的论点<这只是一个效率问题>

我不会盲目追随任何事情。

这是正确的态度。我一直在使用TDD,但我没有像一些人那样严格遵守它。

支持TDD的最佳论据(在我看来)是,当你最终进入项目的重构和维护阶段时,你会得到一组可以运行的测试。如果这是您使用TDD的唯一原因,那么您可以随时编写测试,而不是盲目地遵循方法论。

我使用TDD的另一个原因是编写测试让我提前思考我的API。在写之前,我不得不考虑如何使用一个类。让我的头脑进入这个高水平的项目对我来说很有效。还有其他方法可以做到这一点,如果你找到了其他方法(有很多)来做同样的事情,那么我会说继续做对你有效的事情。

我发现它在单人飞行时更有用。由于周围没有人可以提出想法,也没有人可以进行同行评审,因此您需要确保您的代码是可靠的。TDD/BDD将为您提供这种保证。不过,TDD有点相反。其他人可能完全不同意我的说法。

编辑:我可以补充一下,如果做得好,你实际上可以在编写测试的同时为你的软件生成规范。这是BDD的一大副作用。如果你能独自编写出扎实的代码和规范,你就可以让自己看起来像超级开发人员。

好了,轮到我了。。。我甚至会自己做TDD(对于非尖峰/实验/原型代码),因为

  • 三思而后行:强迫我在开始编写代码之前思考我想做什么。我想在这里完成什么如果我认为我已经有了这件作品。。我怎么能指望它工作呢?"鼓励对象设计中的接口
  • 更容易更改:我可以放心地进行修改当我改变步骤5时,我在步骤1-10中没有破坏任何东西。"回归测试是即时的。"
  • 出现更好的设计:我发现在设计活动中没有投入精力,就出现了更好的设计。测试优先+重构导致了松散耦合的、具有最小方法的最小类。。没有过度设计。。没有YAGNI代码。这些类具有更好的公共接口、较小的方法,并且可读性更强。这是一种禅宗的东西。。只有当你"得到它"时,你才会注意到你得到了它
  • 调试器不再是我的支柱:我知道我的程序是做什么的。。而不必花几个小时来遍历我自己的代码。现在,如果我花10分钟以上的时间在调试器上。。精神警报开始响起
  • 帮助我按时回家我注意到自TDD以来,我的代码中的错误数量显著减少。。即使断言类似于控制台跟踪而不是xUnit类型的AT
  • 生产力/流程:它帮助我确定下一个离散的小步,这将带我完成。。。滚雪球。TDD帮助我更快地进入节奏(或者XPers所说的流程)。我在单位时间内完成了比以前更多的高质量工作。红色-绿色的重构周期变成了。。。一种永动机
  • 我只需按一下按钮就可以证明我的代码是有效的
  • 熟能生巧我发现自己在学习;更快地发现龙。。我有更多的TDD时间。也许是不和谐。。但我觉得TDD让我成为了一个更好的程序员,即使我不先去测试。发现重构机会已成为第二天性

如果我想更多的话,我会更新的。。这就是我在最后2分钟的思考中想到的。

我也是一名合同程序员。以下是我喜欢单元测试的12个原因。

我对TDD的最佳体验集中在pyftpdlib项目上。大部分开发都是由原作者完成的,我也做了一些小贡献,但这基本上是一个单独的项目。该项目的测试套件非常全面,测试了FTPd库的所有主要功能。在签入更改或发布版本之前,将检查所有测试,并且在添加新功能时,也始终更新测试套件。

由于这种方法,这是我工作过的唯一一个没有在新版本后出现showstopper错误、没有签入破坏主要功能的更改等的项目。代码非常扎实,在项目生命周期中打开的错误报告很少,这一直给我留下深刻印象。我(和原作者)将这一成功归功于全面的测试套件以及随意测试每一条主要代码路径的能力。

从逻辑的角度来看,您编写的任何代码都必须经过测试,如果没有TDD,您将自己手动进行测试。pyftpdlib的另一方面,从bug数量和主要问题频率来看,最糟糕的代码是由开发人员和QA手动测试新功能的代码。由于时间紧迫或漏洞百出,事情没有得到测试。旧的代码路径被遗忘了,即使是最古老的稳定特性也会崩溃,主要版本的重要特性也会失效。等等。手动测试对于验证和测试的一些随机化至关重要,但根据我的经验,我认为手动测试和精心构建的单元测试框架都是至关重要的。在这两种方法之间,覆盖范围的差距较小,出现问题的可能性只能降低。

您是否是唯一的开发人员并不重要。你必须从应用的角度来考虑它。所有的应用程序都需要正常工作,所有的应用软件都需要维护,所有的程序都需要少一些bug。当然,在某些情况下,TDD方法可能不适合您。这是最后期限非常快,没有时间执行单元测试的时候。

无论如何,TDD并不依赖于单独或团队环境。这取决于整个应用程序。

我没有大量的经验,但我有看到截然不同的测试方法的经验。

在一项工作中,没有自动化测试。"测试"包括在应用程序中四处寻找,尝试你脑海中弹出的任何东西,看看它是否坏了。不用说,对于扁平的坏代码来说,很容易到达我们的生产服务器。

在我目前的工作中,有很多自动化测试和完整的CI系统。现在,当代码被破坏时,这是显而易见的。不仅如此,在我工作的过程中,测试确实记录了哪些功能在我的代码中有效,哪些还没有。它给了我很大的信心,能够添加新的功能,知道如果我打破现有的功能,它不会被忽视。

所以,对我来说,这与其说取决于团队的规模,不如说取决于应用程序的规模。你能跟踪应用程序的每个部分吗?每一项要求?为了确保应用程序正常工作,您需要运行的每个测试?如果没有测试来证明,那么说应用程序"正在工作"意味着什么?

只有我的0.02美元。

测试允许您在没有破坏系统的情况下进行重构。首先编写测试允许测试定义系统的工作行为。根据定义,任何未由测试定义的行为都是一种副产品,并允许在重构时进行更改。首先编写测试也会推动设计朝着好的方向发展。为了支持可测试性,您发现需要解耦类,使用接口,并遵循良好的模式(例如控制反转),以使代码易于测试。如果您在之后编写测试,您就不能确定您已经在测试中涵盖了系统的所有预期行为。您还发现,由于设计的原因,有些东西很难进行测试——因为它很可能是在没有考虑测试的情况下开发的——并且很容易节省或省略测试。

我通常是独自工作,主要是做TDD——在我没有做到的情况下,我只是没有达到我的实践,或者还没有找到一种适合我做TDD的好方法,例如使用web界面。

TDD不是测试,而是编写代码。因此,它甚至为单个开发人员提供了很多好处。对于许多开发人员来说,编写更健壮的代码是一种思想上的转变。例如,在编写没有TDD的代码后,您多久会想一次"现在这个代码怎么会失败?"?对于许多开发人员来说,这个问题的答案是否定的。对于TDD练习者来说,它将心态转变为在对对象或字符串进行操作之前先检查它们是否为null,因为您编写的测试是专门用来做这件事的(破坏代码)。

另一个主要原因是变化。每当你与客户打交道时,他们似乎永远不会下定决心。唯一不变的就是变化。TDD作为一个"安全网"来帮助寻找所有其他可能突破的领域。即使是在小项目中,这也可以防止您在调试器中浪费宝贵的时间。

我可以继续说下去,但我认为TDD更多的是编写代码,而不是任何东西,这足以证明它是作为一个单独的开发人员使用的。

我倾向于同意你关于"一个开发人员"或"业余爱好"项目的TDD开销的观点的有效性,而不是证明费用的合理性。

然而,您必须考虑到,如果长期持续应用,大多数最佳实践都是相关的和有用的。

例如,TDD在长期运行中为您节省了测试/错误修复时间,而不是在创建第一个单元测试后的5分钟内。

你是一名合同程序员,这意味着你将在当前项目完成后离开,转而从事其他工作,很可能是在另一家公司。您当前的客户必须维护和支持您的应用程序。如果你不给支持团队留下一个好的工作框架,他们就会陷入困境。TDD将有助于该项目的可持续发展。它将增加代码库的稳定性,这样其他经验较少的人就不会在尝试更改它时造成太大的损害

爱好项目也是如此。你可能已经厌倦了,会想把它传给别人。你可能会在商业上取得成功(想想Craiglist),并且除了你之外还会有5个人在工作。

对适当流程的投资总是有回报的,即使它只是获得了经验。但大多数时候,当你开始一个新项目时,你会很感激你决定正确地完成

做某事时,你必须考虑其他人。你必须提前思考,规划增长,规划可持续性

如果你不想这么做——坚持牛仔编码,这样会简单得多。

附言:同样的事情也适用于其他做法:

  • 如果你不评论你的代码,并且你有理想的记忆力,你会没事的,但其他阅读你代码的人不会
  • 如果你不记录你与客户的讨论,其他人将对你做出的关键决定一无所知

etc ad infinitum

如果没有一组合理的单元测试,我将不再重构任何东西。

我不完全使用TDD,先进行单元测试,然后再进行代码测试。我做CALTAL——代码一点点,测试一点点——开发。一般来说,代码是第一位的,但并不总是如此。

当我发现我必须重构时,我会确保我有足够的测试,然后我满怀信心地研究这个结构,我不必把整个旧架构变成我脑海中的新架构计划。我只需要再次通过考试。

我重构了重要的部分。让现有的测试套件通过。

然后我意识到我忘了什么,我又回到了CALTAL开发新东西的过程中。

然后我看到了我忘记删除的东西——但它们真的到处都没用吗?删除它们并查看测试中失败的部分。

就在昨天,在一次大的重构的中途,我意识到我仍然没有完全正确的设计。但测试仍然必须通过,所以我可以在完成第一次重构之前自由地重构重构。(嗖!)这一切都很好,因为我有一组测试来验证更改。

对于单人飞行,TDD是我的副驾驶。

TDD让我更清楚地定义了头脑中的问题。这有助于我专注于实现所需的功能,而不是更多。它还帮助我创建一个更好的API,因为我在编写代码之前要编写一个"客户端"。我也可以重构,而不必担心破坏任何东西。

我会很快回答这个问题,希望你能开始了解其中的一些道理,即使你仍然不同意。:)

如果你足够幸运,能够参与一个长期运行的项目,那么有时你会想,例如,在向上移动之前,先写数据层,然后可能写业务层。如果您的客户端随后进行了需要在数据层上重新工作的需求更改,则数据层上的一组单元测试将确保您的方法不会以不希望的方式失败(假设您更新测试以反映新需求)。但是,您可能也会从业务层调用数据层方法,并且可能在几个地方调用。

假设您在业务层中有3个方法调用,但您只修改了2个。在第三种方法中,您可能仍然从数据层中获取看似有效的数据,但可能会打破几个月前编码的一些假设。这个级别(及以上级别)的单元测试应该被设计为发现破碎的假设,如果失败,它们应该向您强调有一部分代码需要重新访问。

我希望这个非常简单的例子足以让你更多地思考TDD,它可能会激发你考虑使用它的火花。当然,如果你仍然没有意识到这一点,并且你对自己跟踪数千行代码的能力充满信心,那么我没有地方告诉你应该开始TDD。

首先编写测试的要点是它强制执行您正在做出的需求和设计决策。当我修改代码时,我想确保这些代码仍然是强制的,并且很容易"破坏"一些东西,而不会出现编译器或运行时错误。

我有一个测试优先的方法,因为我希望对我的代码有高度的信心。当然,这些测试必须是好的测试,否则它们不会强制执行任何内容。

我有一些相当大的代码库,有很多非琐碎的事情在进行。很容易做出波动的更改,当X不应该发生时,X突然发生了。我的测试多次避免了我犯下可能被人类测试人员忽视的关键(但微妙)错误。

当测试失败时,他们有机会查看它们和生产代码,并确保它是正确的。有时需要修改设计和测试。有时我会写一些能通过百分之九十九测试的东西。没有通过的1测试就像一个同事在审查我的代码(从某种意义上说),以确保我仍然在构建我应该构建的东西。

我觉得,作为一个项目的单独开发人员,尤其是一个更大的项目,你的分布往往很稀疏
您正在进行一次大型重构,突然检测到几个关键的bug,由于某些原因,这些bug在发布前测试中没有出现。在这种情况下,你必须放下所有东西并修复它们,在花了两周时间把头发扯掉之后,你终于可以回到以前做的任何事情了
一周后,你最大的客户之一意识到,他们绝对必须拥有这个酷炫的新闪亮功能,否则他们就不会订购一个月前就应该订购的100万台
现在,三个月过去了,你甚至不记得当初为什么开始重构,更不用说重构的代码应该做什么了。谢天谢地,你在编写这些单元测试时做得很好,因为至少它们告诉你,重构的代码仍然在做它应该做的事。
冲洗,漂洗,重复。

我过去6个月的生活故事

唯一的开发人员应该在他的项目中使用TDD(跟踪记录无关紧要),因为最终这个项目可能会传递给其他开发人员。或者可以引进更多的开发人员

如果没有测试,新用户将很难处理代码。他们会打碎东西。

交付产品时,您的客户是否拥有源代码?如果你能说服他们,通过单元测试交付产品会增加价值,那么你就是在推销你的服务,交付更好的产品。从客户的角度来看,测试覆盖率不仅可以确保质量,而且可以让未来的维护人员更容易地理解代码,因为测试将功能与UI隔离开来。

我认为TDD作为一种方法论不仅仅是"在进行更改时进行测试",因此它不取决于团队,也不取决于项目规模。这是关于在人们开始真正思考如何实现所注意的行为之前,注意到人们对pice代码/应用程序的期望。TDD的主要关注点不仅在于对编写的代码进行测试,还在于编写更少的代码,因为你只需要做一些让测试变得绿色的事情(稍后再进行重构)。

如果你和我一样,发现很难在不考虑如何实现的情况下思考一个部分/整个应用程序的功能,我认为最好在代码之后编写测试,从而让代码"驱动"测试。

如果你的问题不是关于先测试(TDD)或后测试(好的编码?),我认为测试应该是任何开发人员的标准做法,无论是单独还是在一个大团队中,他们创建的代码在生产中停留的时间超过三个月。根据我的经验,这是一段时间,在这段时间之后,即使是原作者也必须认真思考这二十行复杂、超级优化但文档稀少的代码到底做了什么。如果你有测试(覆盖了代码的所有路径),那么思考的就更少了,错误也更少了,甚至几年后。。。

以下是一些模因和我的回应:

"TDD让我思考它会如何失败,这让我成为了一个更好的程序员"

如果有足够的经验,那么高度关注故障模式自然应该成为您流程的一部分。

"应用程序需要正常工作"

这假设你能够测试所有的东西。在正确覆盖所有可能的测试方面,你不会比一开始正确编写函数代码更好。"应用程序需要更好地运行"是一个更好的论点。我同意这一点,但这是理想主义的,不足以像我希望的那样激励人们。度量标准/轶事在这里会很棒。

"为我的<库组件X>工作得很好"

我在问题中说,我看到了这些案例的价值,但感谢你的轶事。

"想想下一个开发者"

对我来说,这可能是最好的论据之一。然而,很可能下一个开发人员也不会实践TDD,因此在这种情况下,这将是一种浪费,甚至可能是一种负担。后门传福音就是它的意义所在。不过,我确信TDD开发人员会非常喜欢它。

当你继承了一个不推荐的必须做的方法论时,你会有多欣赏这个方法论中完成的项目?RUP,有人吗?想想如果TDD并不像大家想象的那么好,那么TDD对下一个开发者意味着什么

"重构要容易得多"

重构是一种与其他技能一样的技能,迭代开发当然需要这种技能。如果我认为新设计从长远来看会节省时间,我倾向于丢弃大量的代码,而且感觉还会有大量的测试被丢弃。哪个更有效?我不知道。

我可能会向任何新的人推荐某种程度的TDD。。。但我仍然对那些已经去过街区几次的人的福利感到困扰。我可能会开始将自动测试添加到库中。这样做之后,我可能会发现做这件事更有价值。

激发自我兴趣。

在我的案例中,"唯一的开发者"指的是小企业主。我写了合理数量的库代码,(表面上)让我的生活更轻松。这些例程和类中的很多都不是火箭科学,所以我可以通过审查代码、对方法进行一些现场测试和调试来确保它们按照我认为的方式运行,从而非常确定它们是否正常工作(至少在大多数情况下)。生活是美好的。

随着时间的推移,这个库不断发展,并被用于更多面向不同客户的项目。测试变得更加耗时。尤其是在我(希望)修复错误并且(更希望)不破坏其他东西的情况下。这不仅仅是针对我代码中的错误。我必须小心添加功能(客户不断要求更多的"东西"),或者确保代码在迁移到新版本的编译器(Delphi!)、第三方代码、运行时环境或操作系统时仍然有效。

更极端的是,我可能会花更多的时间审查旧代码,而不是处理新的(读作:计费)项目。把它想象成软件的静止角度(在未经测试的软件倒下之前,你能把它堆到多高:)。

像TDD这样的技术为我提供了设计更周到、测试更彻底(在客户得到它们之前)并且需要更少维护的方法和类。

最终,这意味着做维护的时间减少了,花更多的时间做更有利可图、更有趣(几乎任何事情)、更重要(比如家庭)的事情。

我们都是有着良好业绩记录的开发人员。毕竟,我们都在读Stackoverflow。我们中的许多人都使用TDD,也许这些人有着良好的记录。我之所以被录用,是因为人们想要一个能写出出色的测试自动化并能教给别人的人。当我独自工作时,我会在家里对我的编码项目进行TDD,因为我发现如果我不这样做,我会花时间进行手动测试甚至调试,谁需要呢。(也许那些人只有良好的记录。我不知道。)

当谈到成为一名优秀的汽车驾驶员时,每个人都认为自己是一名"优秀的驾驶员"。这是所有驾驶员都有的认知偏见。程序员有自己的偏见。像OP这样的开发人员不做TDD的原因将在这个敏捷思想播客系列中介绍。播客档案中还有关于测试自动化概念的内容,如测试金字塔,以及关于什么是TDD以及为什么首先从播客档案中的第9集开始编写测试的介绍。

最新更新