测试驱动开发 TDD

Test-driven development 测试驱动开发

测试驱动开发(TDD)是一个依赖于非常短的开发周期重复的软件开发过程:需求变成非常具体的测试用例,然后软件被改进以仅通过新测试。
这与软件开发相反,软件开发允许添加未经证明符合要求的软件。
美国软件工程师肯特·贝克,谁相信被开发或“重新发现”的技术,在2003年即TDD鼓励简单的设计和十足信心说。
测试驱动开发与极端编程的测试优先编程概念有关,始于1999年,但最近已经创造了更多的一般兴趣。
程序员还将该概念应用于改进和调试 使用旧技术开发的遗留代码。

1 测试驱动的开发周期

1.添加测试
在测试驱动开发中,每个新功能都从编写测试开始。编写一个定义功能或功能改进的测试,应该非常简洁。要编写测试,开发人员必须清楚地了解该功能的规范和要求。开发人员可以通过用例和用户故事来完成此任务,以涵盖需求和异常条件,并可以在适合软件环境的任何测试框架中编写测试。它可以是现有测试的修改版本。在编写代码之后,这是测试驱动开发与编写单元测试的区别特征:它使开发人员专注于之前的需求 编写代码,一个微妙但重要的区别。
2.运行所有测试并查看新测试是否失败
这验证了测试工具正常工作,表明新测试没有通过而不需要新代码,因为所需的行为已经存在,并且它排除了新测试存在缺陷并且总是通过的可能性。新测试应该因预期原因而失败。此步骤增加了开发人员对新测试的信心。
3.编写代码
下一步是编写一些导致测试通过的代码。在这个阶段编写的新代码并不完美,例如,可能会以不雅的方式通过测试。这是可以接受的,因为它将在第5步中得到改进和磨练。
此时,编写代码的唯一目的是通过测试。程序员不得编写超出测试检查功能的代码。
4.运行测试
如果现在所有测试用例都通过,程序员可以确信新代码符合测试要求,并且不会破坏或降低任何现有功能。如果他们不这样做,必须调整新代码,直到他们这样做。
5.重构代码
在测试驱动的开发过程中,必须定期清理不断增长的代码库。新代码可以从方便传递测试的地方移动到更符合逻辑的地方。必须删除重复。对象,类,模块,变量和方法名称应清楚地表示其当前的用途和用途,因为添加了额外的功能。随着功能的添加,方法体可以变长,其他对象变大。
他们受益于分裂和他们的部分仔细命名,以提高可读性和可维护性,这将在以后的日益有价值软件生命周期。可以重新布置继承层次结构以使其更具逻辑性和帮助性,并且可能从公认的设计模式中受益。
重构和创建干净代码有特定的和一般的指导原则。通过在每个重构阶段不断重新运行测试用例,开发人员可以确信进程不会改变任何现有功能。
删除重复的概念是任何软件设计的重要方面。但是,在这种情况下,它也适用于删除测试代码和生产代码之间的任何重复。
例如在两者中重复的幻数或字符串,以使测试在步骤3中通过。
从另一个新测试开始,然后重复该循环以推进该功能。
步骤的大小应始终很小,每次测试运行之间只需编辑1到10次。
如果新代码没有快速满足新测试,或者其他测试意外失败,程序员应该优先撤消或恢复过度调试。
持续集成有助于提供可恢复的检查点。当使用外部库时,重要的是不要增加小到仅仅有效地测试库本身的增量,除非有理由相信库有错误或功能不完整以便为所有库提供服务正在开发的软件的需求。

2 发展风格

使用测试驱动开发有很多方面,例如“保持简单,愚蠢”(KISS)和“ 你不需要它 ”的原则(YAGNI)。
通过专注于仅编写通过测试所需的代码,设计通常比其他方法更清晰,更清晰。

在试验驱动开发实例中,肯特贝克还提出了“ 伪造直到你制造它 ” 的原则。

为了实现一些先进的设计概念,例如设计模式,编写了生成该设计的测试。
代码可能仍然比目标模式更简单,但仍然通过了所有必需的测试。
这一开始可能令人不安,但它允许开发人员只关注重要的事情。

首先编写测试:测试应该在要测试的功能之前编写。据称这有许多好处。
它有助于确保编写应用程序以实现可测试性,因为开发人员必须考虑如何从一开始就测试应用程序,而不是稍后添加它。
它还确保编写每个功能的测试。

此外,首先编写测试可以更深入,更早地了解产品需求,确保测试代码的有效性,并始终关注软件质量。
在编写特性优先代码时,开发人员和组织倾向于将开发人员推向下一个功能,甚至完全忽略测试。
第一个TDD测试可能最初甚至可能不编译,因为它所需的类和方法可能尚不存在。

然而,第一个测试用作可执行规范的开头。
每个测试用例最初失败:这确保测试真正起作用并且可以捕获错误。一旦显示,就可以实现底层功能。
这导致了“测试驱动的开发咒语”,即“红色/绿色/重构”,其中红色表示失败,绿色表示通过。
测试驱动开发不断重复添加失败,传递和重构的测试用例的步骤。
在每个阶段接收预期的测试结果可以加强开发人员对代码的心智模型,增强信心并提高生产力。

保持单位小

对于TDD,单元通常被定义为一个类,或一组通常称为模块的相关函数。声称保持相对较小的单位可提供重要的好处,包括:

减少调试工作量 - 检测到测试失败时,使用较小的单元有助于跟踪错误。
自我记录测试 - 小型测试用例更易于阅读和理解。

测试驱动开发的高级实践可以导致验收测试驱动开发(ATDD)和示例规范,其中客户指定的标准自动进入验收测试,然后驱动传统的单元测试驱动开发(UTDD)过程。
此过程确保客户拥有自动机制来确定软件是否满足其要求。
通过ATDD,开发团队现在有一个特定的目标 - 验收测试 - 使他们不断关注客户真正想要的每个用户故事。

3 最佳实践

3.1 测试结构

测试用例的有效布局可确保完成所有必需的操作,提高测试用例的可读性,并平滑执行流程。
一致的结构有助于构建自我记录的测试用例。
测试用例的常用结构包括(1)设置,(2)执行,(3)验证和(4)清理。

设置:将待测单元(UUT)或整个测试系统置于运行测试所需的状态。
执行:触发/驱动UUT以执行目标行为并捕获所有输出,例如返回值和输出参数。这一步通常很简单。
验证:确保测试结果正确。这些结果可能包括执行期间捕获的显式输出或UUT中的状态更改。
清理:将UUT或整个测试系统恢复到测试前状态。此恢复允许在此之后立即执行另一个测试。

3.2 个人最佳实践

个人可以遵循的一些最佳实践是将常见的设置和拆卸逻辑分离为适当的测试用例所使用的测试支持服务,以使每个测试oracle只关注验证其测试所需的结果,并且设计与时间相关的测试,以允许在非实时操作系统中执行的容差。
允许延迟执行5-10%的保证金的常见做法减少了测试执行中潜在的漏报数量。
还建议以与生产代码相同的方式处理测试代码。
测试代码必须对正面和负面情况正确工作,持续很长时间,并且可读和可维护。
团队可以聚在一起并审查测试和测试实践,以分享有效的技术并捕捉坏习惯

3.3 避免的做法,或“反模式”

测试用例取决于从先前执行的测试用例操作的系统状态(即,您应该始终从已知和预配置状态开始单元测试)。

测试用例之间的依赖关系 – 测试用例彼此依赖的测试套件是脆弱且复杂的。

不应推定执行令 – 初始测试案例或UUT结构的基本重构导致相关测试中越来越普遍的影响。

相互依赖的测试 – 相互依赖的测试可能导致级联错误否定。
即使UUT中不存在实际故障,早期测试用例中的故障也会破坏后续测试用例,从而增加缺陷分析和调试工作。
测试精确的执行行为时间或性能。

建立“无所不知的神谕”-- 随着时间的推移,检查超过必要的oracle会更加昂贵和脆弱。

这个非常常见的错误是危险的,因为它会导致复杂项目中的一个微妙但普遍的时间下沉。

测试实施细节。
运行缓慢的测试。

4 TDD软件

有许多测试框架和工具在TDD中很有用。

4.1 xUnit框架

开发人员可以使用计算机辅助测试框架,通常统称为xUnit(源自1998年创建的SUnit),以创建并自动运行测试用例。xUnit框架提供断言式测试验证功能和结果报告。
这些功能对于自动化至关重要,因为它们将执行验证的负担从独立的后处理活动转移到测试执行中包含的活动。
这些测试框架提供的执行框架允许自动执行所有系统测试用例或各种子集以及其他功能。

4.2 TAP结果

测试框架接受1987年创建的与语言无关的Test Anything Protocol中的单元测试输出。

翻译自 : https://en.wikipedia.org/wiki/Test-driven_development

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值