什么是测试驱动开发 (TDD)?

两名软件开发人员看着电脑屏幕

作者

Josh Schneider

Staff Writer

IBM Think

Ian Smalley

Staff Editor

IBM Think

什么是测试驱动开发 (TDD)?

测试驱动开发 (TDD) 是一种软件开发方法,其中软件测试在其相应功能之前编写。

开发人员为确保通过每次测试而编写足量代码,然后在开展新测试和发布新功能之前对测试与代码进行优化。

本质上,测试驱动开发迫使开发人员放慢速度,在更短的反馈周期内验证和改进其代码。虽然上述操作并非强制要求,但 DevOps 团队鼓励初学者和资深专家在各种编程语言中采用 TDD。例如,Java、Python 等语言,以及应用程序编程接口 (API) 和程序应用。

这种风格的编程加强了编码、测试(以自动化单元级测试的形式)和代码设计之间的关系。虽然测试驱动开发可能会增加前期开发时间,但事实证明,其可以改善代码的功能和灵活性,并节省总体时间。

通过立即识别和处理任何错误,使用 TDD 的开发人员可以防止小问题演变成更大的问题。测试驱动开发迫使开发人员在开发过程中验证和改进他们的代码,从而简化最终的质量检查和更正。

替代测试框架包括在编写所有自动测试之前编写生产代码,或者在编写生产代码之前编写整个测试套件。这些方法虽然不一定无效,但已被证明会增加必要的调试时间,特别是对于更大、更复杂的项目。

虽然测试驱动开发通常用于创建新的生产代码,但它也经常用于改进使用旧技术或其他技术开发的旧版代码的调试过程。

测试驱动开发通过将测试置于开发阶段之前,颠覆了传统的开发流程。作为一项迭代方案,测试驱动开发通过推进可测试的工作流来优化代码质量和可读性,从而在单元级别生成高质量的代码。当开发人员实施单元测试时,他们会专注于一小部分逻辑,例如单一算法。编写专用代码以确保测试结果通过,不仅可以生成更清晰、更耐用的代码,而且还有助于改进文档质量。

测试驱动开发的层次

测试驱动开发主要有两个层次。

验收 TDD (ATDD)

对于验收 TDD(又称“行为驱动型开发 (BDD)”),程序员会编写单个验收测试,然后编写相应的新代码以通过测试。验收测试有时也称为客户测试或客户验收测试。

它们通常可理解为产品利益相关者规定的、实现最低功能要求所需的测试用例。ATDD 致力于确定详细的可执行要求。可以利用各种测试工具执行验收测试,例如 Fitnesse 或 RSpec。

开发人员 TDD

开发人员 TDD 有时简称为 TDD,它要求编码人员编写单个测试,来评估他们自己的 ATDD 测试解决方案。开发人员 TDD 使用测试自动化工具,例如 JUnit 或 VBUnit。

IBM DevOps

什么是 DevOps?

Andrea Crawford 阐述了什么是开发运维、开发运维的价值,以及开发运维实践和工具如何帮助您完成从应用程序构思到生产的整个软件交付管道。本课程由 IBM 资深思想领袖主导,旨在帮助企业领导者获得所需的知识,以优先考虑能够推动增长的 AI 投资。

测试驱动开发周期的 5 个步骤

在实施测试驱动开发战略时,编码人员首先会编写测试来检查软件的各个元素或功能,然后编写相应代码以通过独立测试。完成后,他们会再次测试软件,如果测试通过,则会优化代码(该过程称为“重构”)以精简至仅包含必要元素。然后,开发人员对后续的每个软件功能重复这一过程。

测试驱动开发过程分为五个独立的步骤

  1. 在为特定软件功能编写代码之前,开发人员首先要为该功能编写单独的单元测试。
  2. 然后,开发人员运行测试,这应该会失败,因为尚未编写代码功能。这一步骤对于确认测试本身的功能和不出现误报非常重要。如果代码通过,则表明测试需要重写。
  3. 当程序未通过测试时,开发人员只需编写足够的额外软件代码即可通过测试。
  4. 如果代码通过测试,测试和代码都会经过重构,以简化并消除任何不必要的代码。
  5. 当充分重构后的软件通过重构测试时,开发人员就会转向下一个需要测试的软件功能。然后,测试人员会为每个新功能编写并运行测试。

简而言之,测试驱动的开发过程遵循一个可重复的循环,称为红绿重构循环。该循环的步骤如下:

  • 红色:针对预期软件行为编写失败测试。
  • 绿色:编写足够的额外内容以通过测试。
  • 重构:优化代码,确保其在通过测试的同时尽可能满足简洁性标准。

测试驱动开发的历史

虽然测试驱动开发的具体起源尚不清楚,但先编写测试,然后编写生产代码的概念直到 20 世纪 90 年代中期才成为一种普遍的做法。在此之前,测试框架将开发人员与测试其自己的代码库分开。然而,随着软件工程的发展,DevOps 开发运维团队需要更快、更灵活的方法来满足利益相关者的需求,尤其是在处理快速变化的利益相关者需求时。

测试驱动开发是从各种新型测试框架中发展而来并与其一同演化的,同时也作为模块化组件被应用到各种其他框架中。最值得注意的是,TDD 包含在极限编程 (XP) 的概念中;XP 是一种敏捷软件开发框架,旨在提高软件质量和开发人员的生活质量。

软件工程师 Kent Beck 是敏捷社区的重要人物,也是“极限编程”的创造造者,他被誉为“重新发现”了测试驱动开发。用 Beck 自己的话说

“对 TDD 的最初描述出现在一本关于编程的早期书籍中。它说你拿出输入磁带,手动键入你需要的输出磁带,然后编程直到实际输出磁带与预期输出相匹配。在 Smalltalk 中编写第一个 xUnit 框架之后,我记起了读过这篇文章并进行了试用。这对我来说就是 TDD 的起源。当向年长的程序员描述 TDD 时,我经常听到这样的话:‘当然。要不还能怎么编程?’因此,我把自己的作用称为‘重新发现‘了 TDD”。

测试驱动开发演变过程中的重要时间点包括:

  • 1976 年:Glenford Myers 出版了《软件可靠性》,其中提出“开发人员永远不应测试自己的代码”。虽然 Myers 可能并非这一概念的首创者,但其著作印证了这一此后盛行多年的普遍观点。
  • 1990 年:20 世纪 90 年代初,“黑匣”技术主导了软件测试。在这种测试框架中,测试人员将软件视为一个“黑匣”,难以理解且不可知。关于黑匣测试雇用的测试人员,至关重要的一点是,他们不了解软件的内部工作原理。
  • 1994 年:Kent Beck 开发出 Smalltalk 测试框架 SUnit,为代码库优化的测试优先方法奠定了基础。
  • 1999 - 2002 年:随着敏捷开发运动的兴起,Kent Beck 提出了极限编程的概念,对测试驱动开发进行编码,并引入了模拟对象的关键概念。TDD 在测试期间使用模拟对象来模拟真实依赖项(例如数据库、外部服务等)的行为。此方法可帮助开发人员将其测试代码的重点放在可维护的模拟对象上,这些模拟对象可被验证是否准确执行。使用模拟对象的失败测试可以消除作为失败源的潜在配置错误的依赖项。
  • 2003 年:Kent Beck 发布了《测试驱动开发:示例》,在更广泛的开发社区中普及了这种做法,并进一步使开发人员驱动的测试正当化。

测试驱动开发的优势

作为“极限编程”的一个组成部分,测试驱动开发不仅有利于创建更好的代码,也有利于程序员的技能发展。TDD 可以让编码人员更好地洞察他们的项目并帮助推动程序设计。通过在实现每个功能之前集中测试用例,开发人员必须直观地了解客户或用户将如何使用该功能。这种方法将产品界面置于实施之前,并帮助开发人员创建更多以用户为中心的应用程序。

测试驱动开发的一些额外优点包括:

  • 全面的测试覆盖范围:TDD 有时被视作规范或文档工具,因为这一实践能确保所有代码至少被一个测试覆盖。
  • 完善的文档:出于与提供全面覆盖范围相同的原因,TDD 还能提供完善的文档和规范。该系统可帮助开发人员、项目经理和其他利益相关者验证代码功能与需求,并在整个项目生命周期中确立秩序。
  • 增强信心:使用 TDD 的开发人员和 DevOps 开发运维团队不仅对他们的代码更有信心,而且对他们的测试也更有信心。
  • 促进持续集成:TDD 非常适合持续集成实践,其中实时代码不断更新以添加新功能和补丁。
  • 减少调试:TDD 将测试前置于开发过程,以减少开发末期的大量调试需求。
  • 提高需求清晰度:TDD 可帮助开发人员在投入工作前清楚地了解每个特定程序的需求。
  • 提高生产力:TDD 通常与提高开发人员的生产力密切相关,因为该流程有助于将大型项目分解成更细化、更易实现的步骤。
  • 强化简单设计:绿红重构 TDD 周期的关键第三步要求开发人员重构和简化他们的代码。这种做法改善了总体简便性和质量设计。
  • 强化心智模型:通过检查和集成每个独特的功能或需求,TDD 可帮助编码人员针对现有代码开发强大的心智模型。这一心智模型可以帮助开发人员在开发代码时,实现代码整体功能和需求的可视化。
  • 提高系统稳定性:测试驱动开发的使用已被证明可以通过创建健壮的、经过充分测试的代码来提高整体应用程序稳定性,这些代码符合设计简单性的高标准。

测试驱动开发的挑战

尽管使用测试驱动开发 (TDD) 有许多宝贵的优点,但它并非没有挑战。但这些挑战的严重程度可能取决于项目,或者通过各种其他技术来缓解。TDD 的一些缺点包括:

  • 代码量增加:TDD 要求编码人员不仅为每个所需功能编写代码,还要对每个功能进行测试。将测试代码与产品代码一起添加,结果会导致整个代码库更大。
  • 虚假信心:由于编写每个功能都是为了通过测试,编码人员和项目经理可能会对整体代码的功能产生一种虚假的安全感。即使每个集成功能都经过测试,TDD 也不能取代最终质量控制和 API 测试的需求。
  • 增加管理费用:TDD 不仅要求编码人员生成代码,还需维护大量测试。维护测试代码库需要投入一定数量的资源,并且会增加管理费用。
  • 效率降低:虽然事实证明 TDD 可以提高工作效率,但它可能会拖延项目开发进度,因为它为创建和实施每个新功能增加了步骤。
  • 增加设置时间:TDD 要求开发人员为其代码设置并维护合适的测试环境。
  • 忽视整体设计:虽然 TDD 鼓励简化代码和改进设计,但过多关注单个组件会导致整体代码不太协调。使用 TDD 的编码人员需要了解,当他们的各项功能更新编译到总体软件应用程序时,将如何集成。
相关解决方案
IBM Instana Observability

利用 AI 和自动化的强大功能,主动解决整个应用程序堆栈中的问题。

深入了解 IBM Instana Observability
DevOps 解决方案

使用开发运维软件和工具,在多种设备和环境中构建、部署和管理云原生应用程序。

探索 DevOps 解决方案
云咨询服务

通过我们的云咨询服务持续实现应用现代化,加速业务敏捷性与增长——支持任意平台部署。

深入了解我们的云咨询服务
采取后续步骤

从 IBM Instana® 的主动问题检测到跨堆栈实时洞察,让云原生应用程序保持高可靠运行。

了解 IBM Instana 探索 DevOps 解决方案