什么是测试驱动开发?是否需要进行初始设计?

时间:2010-02-24 15:27:45

标签: tdd

我对TDD很新,尚未开始使用它。但我知道我们必须首先编写测试然后再编写实际代码以通过测试并重构它直到良好的设计。

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

9 个答案:

答案 0 :(得分:8)

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

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

我不能在不了解更多信息的情况下过多地说明它适合您的SDLC,但一个不错的工作流程是:

对于每个用户故事:

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

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

  3. 对于每种行为:

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

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

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

  7. 进入下一个行为。

  8. 转到下一个用户故事。

  9. 当然,您将一直在思考系统不断发展的高级设计。理想情况下,TDD将在较低级别实现灵活的设计,允许适当的高设计随着您的发展而发展,而不是在开始时尝试猜测。

答案 1 :(得分:4)

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

没有实际的理由将设计分成项目的特定阶段。设计一直都在发生。从与利益相关者的初步讨论,通过用户故事创建,估计,然后当然是在您的TDD会议期间。

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

答案 2 :(得分:3)

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

TDD迭代运行良好:

  1. 定义您的初始问题域(可以是小型,进化原型)
  2. 实施
  3. 扩大问题域(添加功能,增加原型)
  4. 重构并实施
  5. 重复步骤3.
  6. 当然,您需要预先具有模糊的架构愿景(技术,层,非功能需求等)。但是,TDD可以很好地引入为您的应用程序带来附加价值的功能。

    请参阅相关问题TDD: good for a starter?

答案 3 :(得分:2)

使用TDD,你不关心设计。我们的想法是,在开始使用有用的设计之前,您必须首先了解您的需求。测试确保您可以在需要决定设计时轻松可靠地更改应用程序。

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

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

从测试开始,您将成为自己的客户。由于您尽量使测试尽可能简单,因此您将创建一个简单的API,使测试工作。

随着时间的推移,您将充分了解您的问题领域,以便能够进行真正的设计。由于您有大量测试,因此您可以更改代码以适应设计。没有在路上打破任何东西。

这就是理论:-)在实践中,你会遇到一些问题,但它的效果非常好。或者更确切地说,它比我迄今遇到的任何其他方法都更好。

答案 4 :(得分:1)

当然,您首先需要一个可靠的功能分析,包括域模型,而不知道您必须首先创建什么,因此无法编写单元测试。

答案 5 :(得分:1)

我使用测试驱动的开发来编程,我可以从经验中说它有助于创建更强大,更集中和更简单的代码。我的TDD配方是这样的:

  1. 使用单元测试框架(我自己编写)编写代码,因为您希望使用它并进行测试以确保返回值等正确。这可以确保您只编写您实际要使用的代码。我还添加了一些测试来检查边缘情况。
  2. 编译 - 你会遇到编译错误!!!
  3. 对于每个错误添加声明,直到没有编译器错误。这可确保您获得代码的最小声明。
  4. 链接 - 您将收到链接器错误!!!
  5. 编写足够的实现代码以删除链接器错误。
  6. 运行 - 您的单元测试将失败。编写足够的代码以使测试成功。
  7. 你已经完成了这一点。您已经编写了实现功能所需的最少代码,并且由于您的测试,您知道它是健壮的。您还可以检测到将来是否破坏了事物。如果您发现任何错误,请添加单元测试以测试该错误(例如,您可能没有想到边缘情况)。而且您知道,如果您为代码添加更多功能,则不会使其与使用您的功能的现有代码不兼容。

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

你需要某种形式的规范,而不是一种设计形式 - 设计是关于如何你要实现的东西,规范是关于什么你要去的实施。

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

例如,“作为客户,我可以开始新的订单”,“作为客户,我可以为我现有的订单添加条目”,等等,如果这就是您的“订单”可能是典型的条目“系统即将发布(如果系统不是”自助服务“用户,那么用户故事就会大不相同,而是用于代表用户输入订单的销售代表使用 - 当然 - 不知道是什么类型订单输入系统意味着,不可能合理地进行,这就是为什么我说你确实需要某种关于系统将要做什么的规范,尽管通常还不是一个完整的想法< em>如何它会这样做。)

答案 8 :(得分:0)

让我分享一下我的观点:

如果你想构建一个应用程序,就需要测试它,例如检查你通过代码检查创建的变量的值,快速删除你可以点击的按钮,然后执行一部分代码并弹出另一方面,TDD会改变您的思维方式,以显示操作结果等对话框。

通常,您只需依靠像visual studio这样的开发环境来检测代码和编译时的错误,并在您脑海中的某个地方,您知道需求,只需通过按钮和弹出窗口或代码检查进行编码和测试。这是一个语法调试驱动开发。但是当你在做TDD时,是一个“语义调试驱动的开发”,因为你首先通过使用测试逻辑(或者一个更加动态和可重复的白板版本)来记录你的应用程序的想法/目标(或者你的应用程序的“语义”,即使你的应用程序通过语法错误(编译时)也会出现语义错误而失败。

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