测试驱动开发_DevOps之浅谈测试驱动开发

测试驱动开发(TDD)是一种设计和开发方法,它强调在编写任何产品代码之前先编写测试。通过不断迭代测试-编码-重构的过程,可以确保软件在整个开发周期内始终保持可运行状态。TDD有助于构建高质量、可维护的软件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

“测试驱动开发(Test-Driven Development, TDD),以测试作为开发过程的中心,它要求在编写任何产品代码之前,先编写用于定义产品代码行为的测试,而编写的产品代码又要以使测试通过为目标。当我们在当前结构中找到最佳设计时,由于有足够的测试做保障,我们可以改动现有设计而不必担心破坏已完成的功能。使用这种开发方法,我们可以让设计更加优良,能编写出可测试的代码,同时还能避免在不切实际的假设基础上过度的设计系统。要得到这些好处,只需不断添加可执行测试,一步步地驱动设计,从而最终实现整个系统简单从好用。 0b45a55c4788b38af290c9d8e36eaf1b.png 何谓测试驱动开发 abcbb8d70a3b30f71bf39c8942c4bc88.png本文所说的TDD为单元测试驱动开发,是敏捷开发中的一项核心实践和技术,也是一种设计方法论。它的主要推动者是 Kent Beck。TDD的周期为“ 测试-编码-重构”。测试先行,然后编码,最后设计。这种“设计后行”的思维方式有别于传统的“ 设计-编码-测试”过程中的设计。这种事后设计的方法可称为 重构,表示把当前的设计转化成一个更佳的设计的过程。TDD的周期也可以用“ 红-绿-重构”表示。

660d31ef3eada89958a88c79231b571f.png

1、先写测试第一步,在写测试也是在做设计。我们是在设计API,即用来访问新功能的接口。编码之前写测试,我们会很自然地考虑新代码的调用方式。注意:测试的粒度应为“正好足以失败”。而不是一次写出整个功能点的测试,再用一个小时写代码让测试通过。 2、写恰好足够的代码新添加的测试之所以会失败,是因为它指出了系统应该有但尚未实现的功能。我们应该只用花几分钟就能实现这项功能,测试失败的状态不应持续太长时间。注意:写恰巧足够的代码是为了让测试尽快通过。因此当前的实现方式可能不是最优。待功能实现、测试通过后,再来解决这个问题的。有测试做保障,就可以进行重构。 3、重构重审现有的代码设计,以改进优化。 0b45a55c4788b38af290c9d8e36eaf1b.png 为何测试驱动开发 abcbb8d70a3b30f71bf39c8942c4bc88.pngTDD是一种设计和开发方法,它能帮我们从项目开始就构建出可运行的软件,以增量的方式添加新功能,使软件在整个开发过程中都能工作良好。通过演进式设计,并且应用重构优化每一步的设计,我们可以防止代码质量随着时间推移而下降。高覆盖率的自动化单元测试又是软件的一道安全网。 TDD能帮我们正确的做事。

一、增量式开发

所有敏捷软件开发过程,即要保证软件可随时可发布,且每天都能持续产生可部署的版本。下图描述的过程中,已完成且测试过的功能不断地累积。在每个时间点上,都只有少量的功能尚未完成或集成。很多项目会推迟交付日期或者交付质量不过关,是因直到最后一刻开发人员还在赶工。TDD可以解决这个问题。在TDD过程中,我们会小步地前进。每一小步都会产生一个工作良好的产品,每一步(以分钟计算)都会距离目标更近一些。

71d4c6b5231a81616c67647c80160486.png

在迭代、增量式的开发过程中,我们需要不断地在“实现新功能”和为支持新功能而“调整设计乃至架构”两项任务间来回切换。演进式设计是指在系统不断添加更多的功能和行为的过程中,不断地微调代码结构。在代码生命周期的任何时刻,代码所展现的都是开发人员为实现现有功能所做的最好的设计。用这种方法,我们可以演化出能经受实践检验的架构。

4f1a497fcba19d3f4bfd6694e78da3ff.png

二、重构以保持代码的健康

在小步前进的过程中,会不断扩展当前的设计以支持新功能,也会不断抛弃旧概念,引进新概念。这种情况下,软件的设计必然会变得很不一致。但通过重构,既可以演进式地设计,又能保持系统设计不出问题。具体重构方法可参考Martin Fowler的著作《重构:改善既有代码的设计》,本文仅介绍重构原则: 1、重构是种纪律严明的方法重构是指用某种严格的方式重构代码。重构或许会显著地改变当前设计,不过这种改变总是小步进行的,而且每一步都会验证代码的行为没有改变。 2、重构是种转换过程重构是两种状态间的转换。在初始状态中,代码的设计存在一定问题,而在目标状态中这些问题都已被修复。 3、重构会改变内部结构重构用于改变系统内部结构—代码,因此大部分重构手法都会涉及实现细节。 4、重构保持原有行为无论怎么修改代码,所变化的都只有代码的设计和内部结构,外部可见的行为和功能会维持原样。换言之,代码的用户应当对重构毫无察觉。如何确定重构没有改变系统的外部行为呢?测试可以确保软件正常运行。

三、通过自动化测试保证软件正常运行

实施TDD过程中会编写大量的单元测试用例。而高覆盖率的单元测试可作为自动化回归测试,成为重构的安全网,保证重构工作不破坏已有的功能。 1、用自动化测试做保护 在整个开发过程中,回归测试会被反复执行许多遍,以保证系统已有功能正常运行。 自动化回归测试必须容易、快速地执行。 如果开发人员在每次微小变动后都运行自动化测试,可以精确地定位问题。 2、快速获得反馈通过与“持续集成”提交构建流水线结合,提交代码前运行与本次改动相关的单元测试,通过质量门禁判断代码能否提交版本管理系统,以快速反馈。 0b45a55c4788b38af290c9d8e36eaf1b.png 如何测试驱动开发 abcbb8d70a3b30f71bf39c8942c4bc88.png如何实施TDD,如何编写及通过测试,首先应从选择测试开始。 1、选择测试技巧
  • 深入细节与整体考虑

细节优先:有利于降低风险,但处理细节问题有时会耽搁总体进度。

整体优先:能够很快的验证总体设计,但推迟了细节方面的工作进度。

  • 探索未知与轻车熟路

减少不确定性是我们选择测试时要考虑的因素之一。也可以先挑选熟悉的测试,再处理不确定性较多的测试。
  • 最大限度地获取价值与摘取现成的成果

应当挑选那些工作量最少,回报却最多的测试。开发基本功能需要更多的时间精力,价值也更大。防御出错的功能、处理null输入,这些都很容易实现,但价值也小很多。
  • 走通基本功能路径(happy path)与先处理出错情况

通常,会先完成基本功能,然后再处理出错情形有时也存在需要把基本功能放在一边,先处理所有的出错情况综上,测试的选择没有一个标准答案,但首先应从简单的测试入手。 2、实现功能技巧使测试通过也有很多途径,在著名的Test-Driven Development by Example一书中,Kent Beck列举了3种实现方式:伪实现(faking it)、三角法(Triangulation)以及显而易见的实现(obvious implementation)。
  • 伪实现

写完测试时,我们并不总是清楚如何正确的实现功能使测试通过。这时候,我们可能会先伪实现某个功能,尽快回到绿的状态。返回硬编码的值可能是最简单的伪实现方法。在伪实现某个功能后,我们可以很容易地切换到“三角法”模式,因为产品代码中绝不能包含硬编码的值,所以要想方设法清除掉硬编码部分,使用真实的实现。为达目的,我们需要写一个新的测试。
  • 三角法

使用三角法来清楚产品代码中硬编码的字符串。若还不清楚如何总结归纳出某段硬编码的应用逻辑,则可以用三角法向正确的实现逐渐演进。“三角法”, 正如电视中的经查们利用手机信号追踪犯罪嫌疑人。警察们从多点检测疑犯的信号进行三角定位。已知观测点的位置,警察们就可以在地图上标出两条线,线的焦点则为疑犯位置。我们每写一个测试,都会在一个维度上约束了可能的解决方案。当测试足够多时,如下图,测试就能有效地缩减解空间,三角定位出我们期望的实现。

507d90b9d60d75d5d5f1445a211e9a0f.png

  • 显而易见的实现

通过测试的方法显而易见时,可以快速前进,直接做实现。 3、测试驱动的基本准则
  • 绝不跳过重构

若不能彻底地重构,代码中仍旧会存在重复。因此,不要跳过重构。
  • 尽快变绿

测试驱动时,要用最简单的方法解决手中的问题。重构时再考虑优化设计。
  • 出错后放慢脚步

开发人员在实践TDD的过程中会自然地逐渐增大步伐,以至于出现错误。这时应小步前进,彻底的重构。 4、提高设计的可测试性在写代码前编写测试确实可提高代码的可测试性,但掌握哪些设计会提高可测试性更为重要。现列举几条提高测试性的设计准则:尽量使用组合而非继承、避免使用static关键字、隔离依赖、注入依赖等。 5、单元测试模式单元测试是TDD中的重要一环。单元测试的粒度,针对的是一个业务场景(或者说是一个分支),即业务上的单元,非技术上的单元。同时除了调用时间过长、访问了外部环境,以及结果不确定的场景以外,其余场景不应该使用mock。对于单元测试所指的“单元”,我们过去有很多不同的理解,比如单元指的是一个类,或者一个方法,此次明确“单元”指的是业务功能单元,将为我们开展单元测试提供了一个清晰的、准确的切入点。其余单元测试相关知识不在本文中介绍。附Mock分类:

c588d0471d3d1c6816027ad481b82cf4.png

0b45a55c4788b38af290c9d8e36eaf1b.png 测试驱动开发与DevOps持续集成流水线结合 abcbb8d70a3b30f71bf39c8942c4bc88.png实施TDD的开发团队必须更频繁地与代码库保持同步,即持续集成。通过在开发者平台配置提交构建流水线和DOD质量门禁,有效检验TDD代码质量。当前已在项目中应用的指标包括:
  • 变更行覆盖率,阈值100%。

  • 增量程序案例执行成功率,阈值100%。

  • 增量技术规范问题,阈值为无主要、严重、阻塞问题。

  • 增量Sonar问题,阈值为无严重、阻塞问题。

fff0275b428aa691a6df6aceaddfb73b.png

代码覆盖率是用来衡量自动化测试对产品代码的语句、分支及表达式等的覆盖程度的指标。在提交构建流水线中检测代码覆盖率,及单元测试执行结果,就可以清楚了解测试的充分性。提交构建报告如下图

0c668a536953d53f35129ee19203474d.png

中心聚富通和智能小象项目组自7月版全面采用TDD研发模式,差旅和党建项目组针对核心功能模块试点TDD研发模式。

一、实施效果

抽取智能小象数据进行TDD效果分析如下: 1、手工测试发现缺陷数量逐月下降智能小象项目自7月版采用TDD研发模式以来测试质量有显著提升,每个版本的手工测试发现的缺陷数量在逐月下降。如下图:

1c9c659b9258f5e4933fb5ca2f3bdf81.png

2、千行代码缺陷密度逐月下降

f081fd42965af9252e9374e8b855fa5d.png

二、通过组织单元测试用例评审保证测试先行度

当前暂未启用开发者平台DOD门禁中的“测试先行”指标,而是在编码前组织由需求、开发、测试共同参与的UTDD Reviw单元测试用例评审,来确保测试先行。并使需求、开发、测试在研发前期对需求达成一致,减少返工率。

三、在敏捷迭代研发模式中应用TDD

在迭代周期内采用TDD的研发模式,附聚富通项目组迭代内研发测试工作详情。

d8bb439f8f509334ac4b8686b7f7b7cf.png

四、下一步工作

1、将继续推动各研发部门需遵照中心测试驱动开发工作指引,做好单元测试脚本设计工作,夯实开发质量,组织好单元测试驱动开发方案与脚本的评审工作。2、对中心测试驱动开发实施情况进行评价,当前已有评价指标包括测试先行度、变更程序覆盖率(变更程序的行覆盖率、变更程序的分支覆盖率、核心程序的变更行覆盖率)、提交功能测试首日准入通过率等。3、调研验收测试驱动开发(ATDD)和行为驱动开发(BDD)。 e6eecba3b423dfc8d43f604f706dbbc6.png

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值