TDD - 如何开始真正思考TDD?

时间:2010-03-25 01:01:17

标签: tdd agile methodology extreme-programming agile-processes

我一直在阅读有关敏捷,XP方法和TDD的信息。

我参与的项目表明它需要进行TDD,但大多数测试都是以某种方式进行集成测试,或者在项目过程中忘记了TDD,以便更快地完成代码。

因此,就我的情况而言,我已经编写了单元测试,但我发现自己将开始编写代码而不是编写测试。我觉得有一个想法/设计/范式的变化实际上是巨大的。所以,尽管人们真的相信TDD,但由于时间压力/项目可交付成果,你实际上最终会回归旧风格。

我有几个课程,我有纯单元测试的代码,但我似乎无法继续这个过程,当模拟进入图片。此外,我有时会看到:“为它编写测试”并不是太微不足道。“

你们怎么认为我应该处理这件事?

19 个答案:

答案 0 :(得分:48)

我觉得有趣的是,到目前为止,没有任何回复能够触及我认为对现代开发实践的基本洞察力,那就是通过收集需求,进行分析和编写软件来编写软件的“老式”方式。在编写任何代码之前对所需系统进行建模实际上有很多功能。

TDD实际上将此体现为

要编写一个首先必须知道的测试 - 用非常简单的术语表达 - 你的输入是什么以及你的预期输出是什么。

一旦掌握了这些知识,你就可以编写一个测试来练习一些神秘的代码,并且在某种程度上,在编写代码本身或创建这些工件之前,代码会与其他工件进行交互。

这就是我们之前称之为“瀑布式”方法中的“需求工程”和“系统分析”。

除此之外,您会发现,一旦掌握了这个级别的要求,编写测试就会自然而然(毕竟,它只是表达在这些要求中体现的功能声明的代码中)。

在编写以测试形式表达需求的代码时,在以可执行代码的形式将这些差距和误解提交给项目之前,您将确定需求中的差距和误解。

对于“敏捷”方法的现代从业者而言,承认他们参与了一系列“瀑布”,我认为这太令人尴尬了,所以这种对需求工程和理解的需求在语言背后晦涩难懂。在拼命避免承认“敏捷”(通常被理解,或者可能被误解)的情况下解决了这些问题,并将大部分洗澡水都扔掉了。

答案 1 :(得分:24)

  

所以,就我的情况而言,我有   书面单元测试,但我发现自己   开始先编写代码   而不是写一个测试。我觉得   有一个想法/设计/范例   变化实际上是巨大的。所以,   虽然一个人真的相信TDD,但是你   实际上最终回归旧式   因为时间压力/项目   交付。

您可能希望尝试在一段固定的时间内(例如几周或一个月)对TDD严格遵守规定。当你在测试之前发现自己编写代码时,删除它并重新开始,测试失败。这样做直到你知道你可以,直到你知道它是什么感觉 - 然后你就有能力做到这一点,以及做出明智选择的自由 - 并且明白如果有时候选择代码就可以了。 。但是,不要让习惯让你远离良好做法。首先得到好的做法,然后选择何时应用它们(当然,你也可以找到“总是”的答案。)

  

我有几个课程,我有纯洁   单元测试代码,但我似乎无法   当嘲笑时,继续这个过程   进入画面。另外,我看到了   时代:“写作并不是一件容易的事   测试它“综合症。”

模拟是一回事 - 你擅长它们吗?如果你对嘲笑感到不舒服,并且你处于嘲讽适合的情况下,那么练习直到 - 就像TDD一样 - 你对他们很好。

关于“太微不足道”的问题 - 不是前几周。只是TDD一直都是。当你发现TDD正在推动你进行更好的设计时 - 即使是“太琐碎”的代码 - 请注意它。想想没有它你的代码会是什么样子。而且您可能会发现没有任何代码对TDD来说太微不足道了。或不。但我的观点是,认真地尝试,在相当长的一段时间内,然后你可以做出更好的选择。熟悉它,然后进行评估。

答案 2 :(得分:15)

很简单:

By learning to think about the "What" __before__ you think about the "How"

换句话说,想一想你想要做什么(界面),而不是你 你将如何做(实施)

作为一种设计工具,TDD作为一种设计工具,基于我的经验,真正帮助您从用户的角度来看待事物而不是编码器的观点。此外,我认为TDD可以帮助您的思维真正思考您正在尝试做什么,预期结果是什么等等。

所以下次当你尝试“TDD”时,问问自己你正在尝试做什么,然后开始编写表达你意图的代码。

示例:

说,有人希望你给它们写一个整数加法器。

没有最小TDD的人只会这样做:

int add(int a, int b)
{
  return a + b;
}

以上代码是否正确?当然是啦。但是,根据我的经验,这种方法在您编写复杂组件时会失败。 (这就是你首先提出这个问题的原因;我知道,我曾经去过那里(也许还在吗?)

关于TDD的好处在于它强迫你首先关注系统的接口(什么),而不是立即要求你实现(如何)。

换句话说,如果有人让我写一个加法器,我会有类似的东西:

void assertOnePlusTwoEqualThree()
{
  assert( add(1,2) == 3 );
}

请注意,即使在考虑add()应该如何工作之前,我已经解决了一些问题。也就是说,我已经想出了:

  • 我的加法器的输入和输出接口
  • 第一个琐碎的测试用例(免费进行单元测试!!)

然后实现add()。

如果这里讨论的逻辑实现起来那么简单,请参阅。要拥有TDD思维模式,就是一直应用它 ,毫无例外。你必须做很多次,甚至不再考虑它。这只是你设计的一部分。我可以这么说,因为我看到它发生在我的专业(它花了大约一年的坚持)。

就像你总是在编程上做得好,无论手头的复杂程度如何,你都可以用相同的方式完成工作。

最后,我认为TDD可与“代码草图”相媲美。也就是说,您开始尝试查看界面是否适用于每个场景(测试用例)。所以,如果你最终改变界面和名称等,那么好的。看,我学到的是很多时候,设计只是彻底地理解问题。 TDD是一种允许您这样做的工具。

人类思维,IMO,通过具体的例子(测试用例/场景)而不是抽象思维(如何实现某些东西)更容易。通过测试用例,它可以让你的思维逐渐了解你想要解决的问题。

可以不知道(回到“传统”方式......放松,让你的思维调整)。如果它不完美也没关系(编写一些实现代码是完全可以的......你的大脑只是试图解决问题)。只是继续尝试,继续阅读,继续编码,但从不放弃! =)

答案 3 :(得分:7)

  

“所以,尽管人们真的相信   TDD,你实际上最终会回去   旧时尚因时间压力/   项目可交付成果。“

实际上,我认为这不可能是真的。

如果你“回归旧式”,唯一的原因可能是因为你不相信TDD会更快地产生更好的代码。

我认为有人会停止使用TDD,因为他们觉得最好更快地生成糟糕的代码。

答案 4 :(得分:5)

尝试练习code kata

  

卡塔是要记住的。   卡塔的学生将其作为一种形式进行研究,   不作为结论。它不是   结论重要的卡塔,   这是导致这一步骤的步骤   结论。如果你想依靠   想想我的想法,设计一下   我设计的方式,你必须学会   以我的反应方式对minutia作出反应。   以下表格将帮助您   去做。当你学习表格时,和   重复一遍,重复一遍,你会的   调整你的身心   回应我回应的方式   导致设计的微小因素   决定。

代码kata让我了解了TDD的感觉。当我开始放松节奏,重新回到旧习惯时,我知道早一点。那时我知道我需要采取更小的步骤,更频繁地运行测试,确保我不会尝试在红色下进行重构等。

答案 5 :(得分:4)

学科。

决定使用TDD并坚持下去,这完全取决于你自己的能力是否能够承诺一个过程并看到它的结论。

答案 6 :(得分:4)

琐碎的代码是bug隐藏的地方。主要是我认为,因为阅读那里的内容而不是那里的 是人的本性。

我一直在努力训练自己在所有新项目中遵循TDD - 这很难打破旧习惯,但我认为值得。由于有更多的前期编码,似乎需要更长的时间,但调试时间是方式

不要考虑在编写代码时如何测试代码。编写测试代码应该如何工作的测试,然后编写使测试通过的最简单的东西。泡沫,冲洗,重复。你的代码可能最终不会像你想象的那样,但肯定会更好。

另外,重要的是,写下你的测试失败。编写通过的测试无助于发现缺陷。这是首先编写测试的另一个非常好的理由 - 如果您先编写代码然后再编写测试,那么您的测试将被编写以传递代码,并且(无意中)偏向于测试代码而不是期望的结果。如果没有正确的代码,你怎么知道你的测试会失败?由于测试只是代码,因此它们也容易受到错误的影响。

首先编写测试,然后逐行编写代码。使用测试断言你的代码是正确的,并使用代码断言你的测试是正确的(是的,你可以确定你的算法是完美的,但测试仍然失败,所有这些都是因为你做了一个愚蠢的错字在测试中。我今天早上做了。)

答案 7 :(得分:3)

购买Kent Beck的“Test Driven Development: By Example”并阅读。

然后,写一个失败的单元测试。

答案 8 :(得分:2)

配对程序执行TDD(理想情况下,某人比你好),

即使是在空闲时间

TDD是其中一项技能,至少在我看来,它比我想象的要容易。当我参与TDD项目时,我的TDD技能总是有所提高,并且当我可以更好地做TDD时,可以向某人指出编程。即使是对那些不了解它的人进行编程也比我做得更好,这有助于我比单独工作更能充实我的想法。

查找(或开始)Code Retreatcoding dojo,特别关注结对编程和TDD。

如果您的公司允许您在公司时间或至少在公司现场组织一次,即使每周只有一个小时,也可以在午餐时间。

答案 9 :(得分:2)

  

你们怎么认为我应该处理这件事?

我建议您认为您的单元测试是与对象的对话框(假设您使用支持OO的语言进行编程,例如Java,C#,Ruby,PHP等)。

创建一个测试用例类,在测试方法中考虑您希望与对象进行的交互。想象一下,您正在使用您作为动词传递给对象的方法与智能机器交谈,然后使用Asserts检查对象是否按照您希望它们做出反应的方式做出反应。

过了一段时间,你可能会非常喜欢这种编程方式,如果没有它你就不会想要编程; - )

答案 10 :(得分:1)

TDD并不是首先编写测试。如果您只想先编写测试,那么您需要测试优先技术。问题在于真空中的测试优先技术(没有TDD)最终会腐烂到比根本没有测试更差的东西。

你说的问题你自己

  

或在项目过程中忘记了TDD更快地完成代码

然而,如果您正在进行TDD,并认识到它必须提供的所有价值,您的团队就会明白,如果没有 TDD,就不可能更快地完成代码,因为这是制作流程的原因首先要快速编程。

我的建议是花一些时间了解它可以给你的价值,方法是识别测试可以替代的工件种类。使用真正的TDD,单个文档可以满足以下所有角色/需求:

  1. 测试(这是否有效?)
  2. 分析(我们想要完成什么?)
  3. 设计(我们将如何完成它?)
  4. 规范(我们怎么知道什么时候完成?)
  5. 终点线(嘿,我们已经完成了!)
  6. 进度报告(嘿,他们完成了吗?)
  7. 过程控制(接下来该怎么办?)
  8. ......等等。我认为,承诺学习TDD - 这是一门很难的学科并且需要大量练习才能掌握 - 需要清楚地了解最后的真正奖品。

    一旦你清楚地了解了这些事情,你就可以开始担心 如何做TDD。

答案 11 :(得分:1)

TDD确实是一种很好的做法(但与此同时我认为我们不应该尝试实现100%的代码覆盖率。)

它的主要优点是:

  1. 它使开发人员考虑API,因为他需要首先使用该API来编写测试。在这一步中,很容易感觉到你的API有问题(坏方法的名称,大多数常见操作需要很多行,太多奇怪的检查异常或相反的用户不可能处理可能的异常)。你可以随意改进并改变它,因为还没有人使用它。

  2. 您想一想您的方法应该如何运作。这往往会在早期阶段打开一些隐含的问题或误解。

  3. 当您进行测试时,您可以很好地测量已完成工作的百分比(即,如果您有20个测试,其中15个已通过,那么75%的工作已完成)。作为开发人员,您对团队或流程更重要。您只需将整个任务划分为许多较小的任务(例如20个),并且可以一个接一个地竞争它们。这比整个事情更令人愉快。

  4. 测试允许您使用代码。你可以进行重构,你可以提高性能等。而且,即使你不会这样做,也很好地意识到你有可能做到这一点。一切都很好,这有点自信。

  5. 如何让自己做TDD?你不需要!如果你想获得所有列出的好处,那么你就不会自己做 - 你只是乐意和自由意志去做TDD。如果你不需要/想要/能够获得它们 - 现在不要这样做。

答案 12 :(得分:1)

如果您遇到大量遗留代码,我发现Working Effectively with Legacy Code非常有用。我认为提高TDD分配的动力,即使它是在对旧的遗留代码进行任何更改之前编写单元测试。从你的问题的暗示看来,这似乎是你的立场。

当然,正如许多其他人指出纪律。经过一段时间强迫你自己,你会忘记为什么你做了另一种方式。

答案 13 :(得分:1)

很多编码实践都需要......练习。

  • 您可以在几个小时内学习并理解OO编程的概念。但是,许多程序员都会承认,只有在练习OO编程约2年后,他们才会突然觉得“流利”。

  • C ++程序员可以在几天内轻松“学习C#”。但它仍然需要大量使用才能“流畅”并理解他们所写内容的全部含义(而不是将语法转换为C#的C ++程序员)。

  • 我最初学会了以“两指”的方式输入。这种方法可以用这种方法快速打字,但我决定尝试学习触摸打字。几个星期以来,我强迫自己度过了使用触摸打字键入所有非常慢的痛苦和挫败感。这很难,而且需要真正的承诺,因为我输入的每一行都只是想用我已经知道的方式来抨击它,这种方式会给我快速的结果。但最终我的触控打字速度与我的双指打字相匹配。

  • 当我第一次使用MFC时,我拒绝了MFC前缀约定(例如“m_”和“p”前缀)。这似乎是丑陋,荒谬和毫无意义的额外打字。然后我强迫自己使用这个系统一个月,在这之后我无法理解我如何编写没有这个命名约定的代码 - 它帮助我更好更快地理解代码,并减少了愚蠢错误的数量。 / p>

那么你如何发展TDD的学科呢?只是承诺做TDD一个月。它会很慢。这很难。只是抨击“旧方式”的几行是很诱人的。但过了一段时间,你将积累经验和速度,这样工作会变得更容易,更自然。然后,您可以评估TDD和非TDD,并确定哪种方法最有效。你可能不会回头。

答案 14 :(得分:1)

你的TDD Mantra当天......

  

TDD中的T是TODO或任务驱动

我更愿意鼓励人们考虑TDD是关于设置任务(又称测试或规范)。 (不完整的任务为红色,任务完成为绿色。)

拥有一个显示测试/规格结果的连续测试运行器是获得测试习惯的主要帮助。就个人而言,我认为这是一个关键的反馈循环,使TDD立即感到富有成效/鼓舞。

当然,在您开始重构和更改代码库的那一刻,您也可以获得有关回归错误的即时反馈,这也是好处的一部分。

答案 15 :(得分:1)

TDD就是知道你在做什么以及你做了什么。如果我有一个正确的TDD设置,那么我可以注释掉一些可能不需要的代码行,然后立即运行测试并找出测试是否确实需要测试 - 或者不是没有测试中断。那些破解测试应该包含足够的描述,我可以理解为什么需要这行代码。

如果没有TDD识别更改代码时会发生什么中断,则需要进行大规模的人工回归测试,然后才能确定您没有破坏某些内容。与全面质量管理的所有部分一样,TDD的目标是在产品线的最后消除大部分(如果不是全部)昂贵的手动测试。怎么样?通过知道你在做什么。怎么样?通过衡量你正在做的事情并研究如何改进它。

如果您发现代码中存在错误并进行修复。问题应该是:它是如何发生的?我可以防止将来发生这种情况吗?怎么样?

这可能是由于需求不佳。这可能取决于您无法控制的因素。关键是不仅要在地毯下刷问题,还要尝试理解并提出如何操作的知识,并将其融入您的流程中。

TDD只是其中的一部分,如果没有兼容和鼓励的环境,它将无法正常运作。

要使这种管理需要购买敏捷方法使长期变化更便宜的基本原则。使用敏捷方法,变更成本会以对数方式上升,而没有敏捷方法的开发会使变更成本随着时间呈指数级增长。

还有更多内容可供阅读。谷歌以下“威廉爱德华戴明”,“全面质量管理”,“变革成本曲线”。

答案 16 :(得分:0)

我的团队正试图更好地遵循TDD方法 - 我不得不说这对于更大的开发团队来说并不是最简单的事情。

团队中的每个人都需要在编写任何生产代码之前先考虑他们的测试优先。这可能很难,因为尽管人们可能会说他们会这样做,但一旦他们到达键盘就无法保证。

对于一些管理人员来说,看起来TDD可能是一种昂贵的方法,因为我认为编写一些东西可能需要稍长的时间(这就是豆类计数器所看到的),但这是长期的“痛苦” - 期限收益。然后,您(希望)有一个编写良好,经过验证的代码,并且有适当的机制来在将来发生变化时发现错误。

对于任何不相信的人,请通过示例给TDD一个读取并尝试重复您的软件。

答案 17 :(得分:0)

如果你处于真空状态,TDD很难开始使用,而当团队中的其他人只对TDD提供口头服务时,几乎不可能做到这一点。我的建议是找到一个地方(在工作中或工作地点),你可以自己编程,或者更好地与一个忠诚的朋友或同事一起编程,然后开始一直这样做 - 无一例外地,无一例外。

TDD(以及敏捷就此问题)只有当您和您的团队都支持并致力于使其发挥作用时才开始点击。你需要获得一些TDD胜利,它会让你相信你。然后回到你的其他项目,成为它的冠军。从某个地方开始,你可以获得那些至关重要的早期胜利。它完全不同。

答案 18 :(得分:-1)

IMO,带有测试/代码/重构的TDD - 听起来很棒&有趣但是:

  • 没有必须按顺序完成的硬性规则
  • 很多次,我发现自己编写了代码&然后写测试&找到我错过的有趣的小块
  • 其他时候,我去测试>代码>重构循环&享受它一样。
  • 我认为重要的方面是使用经过单元测试的代码&足够的覆盖范围,以确保涵盖所有重要的路径。实现它的顺序并不那么重要。

HTH。

编辑:进一步澄清我的观点

  • 虽然我更喜欢从编写测试开始,但有时通过编写代码(特别是对于新功能和/或新项目)更容易理解景观。然后写测试
  • 但是,如果我正在重构代码和/或修复错误,我将首先检查测试是否已写入&它们足以涵盖代码所做的事情。如果没有,首先我会写测试&然后进行重构。