【问题标题】:What is Test Driven Development? Does it require to have initial designs?什么是测试驱动开发?是否需要有初始设计?
【发布时间】:2010-02-24 15:27:44
【问题描述】:

我对 TDD 很陌生,还没有开始使用它。但我知道我们必须先编写测试,然后才是真正的代码才能通过测试并重构它直到好的设计。

我对 TDD 的担忧在于它适合我们的 SDLC。假设我需要制作订单处理系统。现在,没有这个系统的任何模型和设计,我该如何开始编写测试?我们不应该需要定义实体及其属性才能继续吗?如果没有,是否有可能在没有任何设计的情况下开发大系统?

【问题讨论】:

标签: tdd


【解决方案1】:

有两个级别的 TDD,ATDD 或验收测试驱动开发,以及由单元测试驱动的普通 TDD。

我猜 TDD 和设计之间的关系受到源代码是软件产品设计的某种“敏捷”概念的影响。许多人通过将 TDD 翻译为测试驱动设计而不是开发来强化这一点。这很有意义,因为 TDD 应该被视为与驱动设计相比更多地与测试有关。最后进行验收和单元测试是一个很好的副作用。

如果不了解更多关于它适合您的 SDLC 的位置,我真的不能说太多,但一个不错的工作流程是:

对于每个用户故事:

  1. 使用FitNesseCucumber 之类的工具编写验收测试,这将从用户理解的角度指定给定输入的期望输出。此级别使规范自动化,甚至可以在理想情况下替换规范文档。

  2. 现在您可能对类/行为等可能需要的软件设计类型有一个模糊的概念。

  3. 对于每种行为:

  4. 编写一个失败的测试显示您希望如何调用代码来使用该类

  5. 实现使测试通过的行为

  6. 重构测试和实际代码以反映良好的设计。

  7. 进入下一个行为。

  8. 进入下一个用户故事。

当然,您会一直考虑系统不断发展的高级设计。理想情况下,TDD 将导致较低级别的灵活设计,允许适当的高级设计随着您的发展而发展,而不是在一开始就试图猜测它。

【讨论】:

  • @Kurt 我见过的关于 TDD 的最佳答案。恭喜。 (+1)
  • 那么所有的模式和设计原则呢。在编写测试和实际代码并行时,我们如何确定在哪里应用什么模式和设计原则。
  • 如果每个开发人员都按照自己的风格进行开发,我们的软件产品会处于什么位置。大多数时候,某些部分由一个团队开发,而其他部分由另一个团队开发。它会引起组件之间的兼容性问题。
  • @Nirajan 识别模式和设计原则来自学习和经验。 TDD 实际上有助于自然地避免很多反模式。这是一个非常详细的主题,但 Robert C. Martin 等人的书籍是关于该主题的重要知识来源。跨不同的团队一起工作也是一个很大的话题。太大而无法在此评论中解决,但在功能的垂直切片中工作而不是层会有所帮助。沟通是避免问题的关键,而作为 TDD 副作用的测试有助于更快地识别它们。
  • 感谢您的好建议。实际上,我为演示应用程序编写了一些测试。如何决定我们需要在多大程度上重构代码。由于我是 TDD 新手,在没有足够知识的情况下在我的项目中使用它是一种好习惯吗?
【解决方案2】:

它应该被称为测试驱动设计,因为它就是这样。

没有实际理由将设计分成项目的特定阶段。设计无时无刻不在发生。从与利益相关者的初步讨论,到用户故事的创建、估计,当然还有在您的 TDD 会议期间。

如果您想使用 UML 或其他方式将设计形式化,那很好,请记住代码设计,其他一切都只是一个近似值。请记住,YAGNI 适用于所有内容,包括设计文档。

【讨论】:

    【解决方案3】:

    首先编写测试迫使您首先考虑问题领域,并充当一种规范。然后在第二步中,您移至 解决方案域 并实现该功能。

    TDD 可以很好地迭代:

    1. 定义您的初始问题域(可以是小型进化原型)
    2. 实现它
    3. 扩展问题域(添加功能,扩展原型)
    4. 重构并实现它
    5. 重复第 3 步。

    当然,您需要预先有一个模糊的架构愿景(技术、层、非功能性需求等)。但是,为您的应用程序带来附加值的功能可以通过 TDD 很好地引入。

    查看相关问题TDD: good for a starter?

    【讨论】:

      【解决方案4】:

      使用 TDD,您不必太在意设计。这个想法是,你必须先了解你需要什么,然后才能开始一个有用的设计。这些测试可确保您在需要决定设计时能够轻松可靠地更改应用程序。

      如果没有 TDD,就会发生这种情况:您进行了一项设计(这在某些领域可能过于复杂,而且您忘记考虑一些重要的事实,因为您不了解它们)。然后开始实施设计。随着时间的流逝,您会意识到设计的所有缺点,因此您会对其进行更改。但是改变设计并不会改变你的程序。现在,您尝试更改代码以适应新设计。由于编写的代码不容易更改,因此最终会失败,留下两种设计(一种损坏,另一种处于未知状态)和两种都不适合的代码。

      要从 TDD 开始,请将您的需求转化为测试。为此,请询问“我如何知道满足此要求?”当你可以回答这个问题时,编写一个测试来实现这个问题的答案。这为您提供了(要编写的)代码必须遵守的 API。这是一个非常简单的设计,但是 a) 始终有效,b) 灵活(因为您无法测试不灵活的代码)。

      同样,从测试开始,您就会变成自己的客户。由于您努力使测试尽可能简单,因此您将创建一个简单的 API 以使测试正常工作。

      随着时间的推移,您将充分了解您的问题领域,从而能够进行真正的设计。由于您有大量测试,因此您可以更改代码以适应设计。不会在途中最终破坏任何东西。

      这就是理论 :-) 在实践中,您会遇到一些问题,但效果很好。或者更确切地说,它比我迄今为止遇到的任何其他产品都好用。

      【讨论】:

      • 但客户在给出要求时,总是要求估算实施。是否可以在没有设计的情况下进行估算。众所周知,编写测试和实际代码是并行任务,当我们如何进行估计时。
      • 您从谈论测试开始:客户必须帮助您充实它们(需要测试什么,预期结果是什么等)。这将使您很好地了解每个功能需要多少测试,从而了解功能的“大”或复杂程度。
      【解决方案5】:

      当然,您首先需要一个可靠的功能分析,包括域模型,但如果不知道首先要创建什么,就不可能编写单元测试。

      【讨论】:

      • 只有你不能先做一个“可靠的功能分析”,因为你需要知道你最终需要什么,即它预测未来 -> 不起作用。如果有人要求进行“可靠的功能分析”,他们会得到“我们现在所知道的一样好”,然后他们想知道为什么设计有所有这些讨厌的缺陷。
      【解决方案6】:

      我使用测试驱动的开发进行编程,我可以根据经验说它有助于创建更健壮、更集中和更简单的代码。我的 TDD 方法是这样的:

      1. 使用单元测试框架(我自己编写)编写您希望使用的代码并测试以确保返回值等正确。这确保您只编写您实际要使用的代码。我还添加了一些测试来检查边缘情况。
      2. 编译 - 你会得到编译错误!!!
      3. 为每个错误添加声明,直到没有编译器错误。这可确保您拥有最少的代码声明。
      4. 链接 - 您将收到链接器错误!!!
      5. 编写足够的实现代码来消除链接器错误。
      6. 运行 - 你的单元测试将失败。编写足够的代码以使测试成功。

      此时您已经完成了。您已经编写了实现功能所需的最少代码,并且由于您的测试,您知道它是健壮的。您还可以检测到您将来是否破坏了某些东西。如果您发现任何错误,请添加一个单元测试来测试该错误(例如,您可能没有想到边缘情况)。而且您知道,如果您在代码中添加更多功能,您不会使其与使用您的功能的现有代码不兼容。

      我喜欢这种方法。让我感到内心温暖而模糊。

      【讨论】:

        【解决方案7】:

        TDD 意味着有一些现有的设计(外部接口)可以开始。为了开始编写测试,您必须考虑某种设计。有人会说,TDD 本身不需要那么详细的设计,因为编写测试的行为为设计过程提供了反馈,但这些概念通常是正交的。

        【讨论】:

          【解决方案8】:

          您需要某种形式的规范,而不是某种形式的设计——设计是关于如何你去实现某事,规范是关于你将要进行的什么实施。

          我见过的与 TDD(和其他敏捷流程)一起使用的最常见的规范形式是 user stories——一种非正式的“用例”,往往用一些刻板的英语句子表达,例如“As a ,我可以“(用户故事的形式或多或少有些僵化,具体取决于所使用的确切风格/流程)。

          例如,“作为客户,我可以开始新订单”、“作为客户,我可以在我的现有订单中添加条目”等等,如果这是您的“订单”,则可能是典型的entry”系统是关于(如果系统不是为用户提供“自助服务”,而是供销售代表代表用户输入订单时使用,那么用户故事将大不相同——当然,不知道是哪种类型)订单输入系统的意思是,不可能明智地进行,这就是为什么我说你确实需要一些关于系统将要做什么的规范,尽管通常还没有一个完整的想法如何它会这样做)。

          【讨论】:

            【解决方案9】:

            让我分享一下我的观点:

            如果您想构建一个应用程序,在此过程中您需要对其进行测试,例如检查您通过代码检查创建的变量的值,或者快速放下一个您可以单击的按钮,然后执行部分代码并弹出打开一个对话框来显示操作的结果等。另一方面,TDD 改变了你的心态。

            通常,您只需依靠 Visual Studio 之类的开发环境在编码和编译时检测错误,在您的头脑中某个地方,您知道需求,只需通过按钮和弹出窗口或代码检查进行编码和测试。这是一个语法调试驱动的开发。但是当您进行 TDD 时,是“语义调试驱动的开发”,因为您首先通过使用测试逻辑(或“语义”),并且在出现语义错误时失败,即使您的应用程序通过了语法错误(在编译时)。

            在实践中,您可能不知道或拥有构建应用程序所需的所有信息,因为 TDD 会迫使您首先编写测试,因此您不得不在应用程序的早期阶段就应用程序的功能提出更多问题开发而不是构建很多只是为了发现不需要你写的很多东西(或者暂时不需要)。你真的可以避免在 TDD 上浪费你宝贵的时间(尽管一开始可能不是这样)

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-01-30
              相关资源
              最近更新 更多