【问题标题】:Explain TDD in simple terms用简单的术语解释 TDD
【发布时间】:2023-03-05 05:43:01
【问题描述】:

我在浏览 StackOverflow 时遇到this 问题。作者在这里提到了他/她的调试风格:

我想知道如何进行调试。目前我遵循的步骤是,

  • 我完成了一个大剧本,
  • 评论除我要检查的部分以外的所有内容
  • 执行脚本

在其中一个答案中,另一位用户说提问者的调试方式错误:

你的顺序在我看来完全倒退了。这是我的做法:

  1. 我为我想要的功能编写了一个测试。
  2. 我开始编写脚本、执行位和验证测试结果。
  3. 我回顾了我为记录和发布所做的工作。

我对编程很陌生,我遵循第一种做事方式。第二种方式似乎被称为测试驱动开发,它似乎是一种非常低效的做事方式。

您能否以更简单的方式解释 TDD 及其优点?

【问题讨论】:

  • “低效”?真的吗?你为什么这么说?请提供一些证据或信息来支持这一点。
  • 很抱歉我的措辞有误。我的意思是说,从表面上看,我假设它效率低下。但实际上我从未尝试过使用它。

标签: tdd


【解决方案1】:

这里有两件事混合在一起。测试驱动开发和单元测试。您不必同时执行它们——首先编写集成测试称为 ATDD 或 BDD,然后在代码编写单元测试后编写单元测试只是简单地编写单元测试——但它们可以很好地协同工作。

单元测试是关于测试一小段代码(通常是一个方法,但一个单元是一个灵活的野兽)并单独测试它。采用我们上面最初的两种方法,这会将其更改为:

  • 我完成了 [section of a] 大型脚本,
  • 我编写的测试只针对我认为有问题的区域
  • 执行测试

这意味着您不会修改脚本,因此不要冒忘记取消注释的风险。您还有一个可以再次运行的测试。这意味着您可以合理地确定新更改不会重新启用此错误。这很重要。只运行一次的测试是没有用的。测试应该是可重复的,而破解代码的某些部分是无法做到的。

测试驱动开发就是在编写代码之前编写测试。这确实增加了很大的开销,但是您认为什么时候修复错误最便宜?当你在写那段代码时还是几个月后?这是我使用 TDD 的主要原因。我在编写代码时会收到有关我的代码的反馈。更不用说将它发送给测试团队,等待一周让他们针对它运行一组测试,然后不得不提取不同的用户故事来尝试弄清楚我一周前到底在想什么。

它们还意味着您可以重构并合理地确保您不只是将所有内容都塞满,帮助您思考代码的设计、充当文档和其他各种可爱之处。但对我来说,TDD 的杀手锏是即时反馈。

【讨论】:

    【解决方案2】:

    简而言之,保存您的测试。

    许多优秀的程序员在进行测试时会进行测试,但他们不会保存测试。他们只是编写少量代码来测试他们编写的代码。从“测试”作为动词(例如我测试了代码)到“测试”作为名词(例如,这是对代码的测试。)

    TDD 不仅如此,但这是一个好的开始。

    【讨论】:

      【解决方案3】:

      TDD 意味着让您的测试驱动您的开发过程。一开始可能看起来效率低下,但我会在稍后讨论。

      流程基本上和上面那个人描述的一样:

      1. 编写测试 - 测试您想要的部分功能(但尚不存在)
      2. 编写最少量的代码 - 填写足够的内容以使您刚刚编写的测试通过
      3. 重复 - 返回第 1 步,直到您拥有所需的所有功能

      这通常意味着编写一个引用对象的测试,然后编写该对象的定义。然后编写一个在该对象上使用方法的测试,然后编写方法本身。你的工作时间间隔很短,从测试到编码来回走动(短 = 几秒到每边大约一分钟)。

      TDD 的理念是您需要 100%(理想情况下)的测试覆盖率。从逻辑上讲,如果您想编写测试,您可以选择在实际编写功能代码之前或之后编写它们。在编码之前做的好处是:

      • 您实际上是在测试您的测试,因为它们应该会失败(当您编写它们时)然后通过(当您编写代码时)
      • 您保证始终拥有大量的测试覆盖率
      • 当您进行重构时,您知道您有很多测试覆盖率来确保您不会做一些愚蠢的事情(这是我看到的最大优势,因为很多时候您都在处理您已经忘记的旧代码所有的微妙之处)

      有些人说这有助于他们清楚地定义他们将要做什么,因此他们实际上可以更快地填写代码。即使需要更长的时间,当您返回该代码并稍后重构/更改/优化它时,构建测试覆盖率似乎确实有利。而且,无论您决定在编码之前还是之后进行测试,我想我们都同意在某种程度上进行测试(请不要在这里进行单元测试宗教辩论)很重要。

      【讨论】:

        【解决方案4】:

        这对你来说似乎很有效:

        • 我完成了一个大剧本,
        • 评论除我要检查的部分以外的所有内容
        • 执行脚本

        不是。如果您没有事先进行设置,那么“您要检查的部分”可能无法正常工作。在运行测试之前弄清楚要评论的内容以及不评论的内容(只需一次!)很难做到正确,并且会花费大量时间 - 并且可能会给您留下假阳性结果(您认为您的代码正在运行,但它不是)。正确取消注释生产代码会带来另一个错误风险。每次你想检查你的代码时,你都必须这样做——而且你很可能没有检查其中有错误的代码,认为它太简单而不会出错。

        使用 TDD - 使用测试驱动设计和单元测试 - 我们避免了所有这些陷阱。我们正在编写微小的测试,因此它们不会花费太多时间来编写,而且它们只能测试微小的功能。这驱使我们大多数人编写小函数和小类;那是(部分)好的设计;它促进重用并减少耦合。而且,作为一个副作用——我怎么强调它只是一个副作用,而不是主要的好处——我们得到了全面的测试覆盖率。我们的测试小而快,每天可以运行数百次,所以当我们把事情搞砸时,它们会让我们知道。而且因为它们很小而且很专注,它们不仅仅告诉我们我们搞砸了;他们告诉我们什么我们搞砸了。

        好的设计:微小的方法、微小的类、最小的耦合;只有我们需要满足我们实际需求的代码;全面的自动化单元测试覆盖率。 这是高效的。

        【讨论】:

          【解决方案5】:

          我无法用恰当的术语来解释它,但我可以告诉你,总的来说,TDD 确实允许开发人员专注于任务的实际需求。它相当于另一种强制要求非常具体和详细的​​方法。一旦测试完全充实,任何成功完成测试的代码就完成了。时期。该任务的开发结束。

          这是否一定比其他方式更有效或更低?我不这么认为,在一个完美的世界里。但我们大多数人并不生活在一个完美的世界中。因此,在我看来,如果唯一能够以某种方式以 100% 准确度表示需求的人是测试创建者,那么它就可以正常工作。程序员不会花时间去猜测要做什么,他们不会为不可能发生的条件而写,等等。

          我是 TDD 的支持者,但我不得不承认我从未在一个这样做的组织中工作过。

          【讨论】:

          • 但是当需求改变时会发生什么?我们都知道需求不是一成不变的,所以我们必须重新编写测试吗? (我不确定我的评论是否有意义,因为我对 TDD 知之甚少)
          • 你是对的——他们改变了。但是 TDD 还要求您在一定程度上进行自动化测试,这意味着当需求发生变化时,您只更改与更改相关的测试。所有其他的都保留下来,(正如许多人所指出的,拥有一套单元测试是非常有益的)。
          • “我不这么认为,在一个完美的世界里。”这个“完美世界”是什么?在一个完美的世界里,编程很难,测试也很难。首先编写代码——没有任何测试——在任何世界都是浪费时间。你能澄清一下这个神秘的“完美世界”是什么吗?我不明白你在说什么“完美世界”。
          • @S.Lott :我想说的是,在需求 100% 完整、准确和清晰的世界中,TDD 不一定更有效,并且程序员首先 100% 正确理解需求时间。
          • @MJB:“要求已 100% 完成”?不是一个“完美”的世界。一个“幻想”的世界。我什至不确定“100%”的完整要求是什么意思。如果需求如此完整,您可以简单地编写代码,那么它们将是目标语言的详细设计 - 到编码级别。与代码同构;因此不是“要求”。我想你的意思是“幻想”世界之类的。
          【解决方案6】:

          网络上有无数关于 TDD 的文章。我使用 TDD 才一年,所以我不是专家,但这里是我为什么喜欢 TDD 的简短总结:

          • 首先关注您的目标,而不是实施
          • 它让您实现完成任务所需的最小代码段,仅此而已。您可以稍后将其重构为更漂亮的东西,但通常您更倾向于遵循YAGNI 原则。
          • 最重要的是:您可以毫不畏惧地重构您的代码,因为您知道在上次重构期间您没有破坏任何东西(这假设您有良好的测试覆盖率,但这是按照预期方式使用 TDD 的一部分)。这使您更加敏捷。
          • 如果您有良好的测试覆盖率,那么频繁部署到生产环境的风险也会降低。

          虽然 TDD 需要一些时间来适应它,但您会发现它实际上并不比传统编码效率低。恰恰相反,因为从长远来看,您往往会花费更少的时间来调试代码。

          话虽如此,我不是 TDD 强硬派。在某些情况下,我更喜欢在编写实现之后编写单元测试。当我使用新技术时,它比 TDD 更容易,并且在我实现它之前不知道所需的结果是什么样的(例如 ASP.NET MVC 中的 ViewResult 对象)。

          【讨论】:

          • +1“完成任务所需的最小代码段,仅此而已”这就是效率。
          【解决方案7】:

          我遵循第一种做事方式。

          很多人也是这样

          ...第二种方式...似乎是一种非常低效的做事方式。

          所有新的和不同的东西似乎效率低下。习惯别人都错而你是对的感觉。

          你以后的职业生涯都会充满这种感觉。每一件新事物都显得效率低下。而且总是显得效率低下。

          你能用更简单的方式解释一下 TDD 及其优点吗?

          是的。

          TDD 更高效,因为它更高效。

          必须进行测试。您可以先或最后编写测试。无论哪种方式,您都必须编写它们。

          您可以“评论除我要检查的部分之外的所有内容”并尝试以缓慢、无效的方式定位其中的错误。它通常是无效的,因为 - 如果没有测试来推动您的开发 - 您可能会编写无用或浪费时间的代码。

          或者您可以编写一个测试并以有效的方式编写通过测试的最少代码

          【讨论】:

            【解决方案8】:

            TDD 还可以帮助您获得出色的测试覆盖率,并迫使您构建结构良好、分离的代码 => 良好的可测试性。

            如果您构建一个 100% TDD 的系统,您将对所有内容进行测试,这意味着您的系统对更改将更加安全,并且将来实现错误的可能性会小得多。

            开发需要更长的时间,但从长远来看会提供更好的结果。

            【讨论】:

              【解决方案9】:

              你应该编写单元测试:

              • 单元测试为自己买单,因为它们减少了错误的数量。
              • 代码应该按原样进行测试,测试应该很容易在现在和将来运行,所以注释一切的测试方法已经过时了。
              • 您应该测试一些功能,以防止事情变得过于复杂而无法测试。

              您是在编写代码之前还是之后执行此操作并不那么重要。有人说,如果你在实现之后再写测试,你就改变你的测试来适应实现,这降低了单元测试的有效性。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2017-05-12
                • 1970-01-01
                • 1970-01-01
                • 2011-03-21
                • 1970-01-01
                • 2021-02-28
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多