满纸荒唐言,一把心酸泪,都云作者痴,谁解其中味。 技术博客 心情随笔 登录
测试驱动开发(TDD)浅析
2025/2/3 1604

导航

1前言

2为何要使用测试驱动开发?

3测试驱动开发的前置条件

4测试驱动开发的实施步骤

4.1产品需求

4.2软件设计

4.3软件开发

5UTDD简介

6结束语

1 前言

测试驱动开发(TDD:Test Driven Development)是敏捷开发中的一项核心实践,推崇通过测试来驱动整个开发的进行。TDD有别于传统“先编码,后测试”的开发过程,而是要求在编写业务代码之前,先确认测试用例。TDD的概念大致在上世纪90年代随着极限编程(XP:Extreme Programming)提出,但在敏捷开发已大行其道的今天,TDD仍未普及,对其也是褒贬不一,存在一定争议。

我将TDD理解为ATDD(Acceptance Test Driven Development,验收测试驱动开发)和UTDD(Unit Test Driven Development,单元测试驱动开发)两个概念。ATDD指的是在编码之前先明确验收标准,然后功能开发围绕验收标准展开,它符合我们的常识,一件事情在实施之前先明确要达成的目标。而UTDD则是更激进的单元测试驱动开发,它有一个著名的红绿蓝规则,大致思想是先编写单元测试用例,通过单元测试用例来驱动业务逻辑编码,这与常规的编码有很大不同,当然它也有独道的优势。

本文主要根据实际项目中的痛点,围绕ATDD的思想,探讨如何通过测试前置解决项目中存在的问题,为了表述方便,后续本文中的TDD均指代ATDD。来源:https://www.wubayue.com

2 为何要使用测试驱动开发?

为何要使用测试驱动开发

驱使我们在项目中实施TDD的主要原因来自项目中的痛点,但每个团队遇到的问题各不相同,因此本文仅作参考,如有雷同欢迎对号入座共同探讨。

架构设计师

我们的人员结构中,并没有专职的软件架构师,功能按人头分配,大多数情况下设计开发是同一个人,这样就导致了软件架构设计能力参差不齐,软件设计质量无法得到保障,甚至风格无法统一,更无法对软件设计质量进行量化。我的想法是,首先构建项目架构的基础约束,比如基于DDD(Domain Driven Design,领域驱动设计)的业务层级组织、一定的抽象、松散耦合策略等,然后把担任架构设计角色的开发工程师们集中培训学习,让他们理解项目中的设计模式和方法。最后借助TDD,在软件设计环节将每个功能拆解到业务抽象层的类和方法签名,比如某个功能由哪些类哪些方法构成,这些方法的入参出参是怎样的。这样在架构设计环节,就得到了相对可靠,具备一致性,以及可量化的设计。在当前项目背景条件下,TDD能帮助我们解决这些软件设计过程中存在的问题。

开发工程师

项目中的开发工程师半数为应届毕业生,但即使是有经验的开发工程师,代码质量也参差不齐。除了受软件设计质量影响,设计对编码的约束力弱也是导致代码质量不高的主要原因,开发工程师只关注实现功能,可能会参考设计,也可能会自由发挥,编码和设计脱钩。借助TDD,我想可以改观这种情况,设计不再是一份仅供参考的文档,而是强制渗透到代码层面,开发工程师主要做的事情就是对每个抽象接口进行具体编码实现,软件设计和编码实现两个环节完全扣合,也符合DDD(领域驱动设计)在代码中传承业务知识的理念。

在项目实施过程中,还有一个令开发工程师和测试工程师都很头痛的“研发自测”环节,测试工程师为了确保每个提测版本的质量和测试效率,要求开发工程师在提测前,先依照测试用例自已对功能进行测试。这是一个合理的要求,但在实际执行时面临诸多问题,比如自测的质量取决于开发工程师是否细心,也取决于开发工程师的闲忙,闲时认真负责,忙时囫囵吞枣。我想通过TDD持续集成中的自动化测试,完全可以去掉这个环节。

测试工程师

在测试环节,当前我们仍以人工手动测试为主,人力资源消耗大且稳定性不高,希望借助TDD,全面转变为自动化测试为主,手动测试为辅。

令测试人员烦恼的另一个问题是开发人员时常改动代码,又未能准确评估输出改动影响点,这样就导致漏测和BUG流出。而在TDD模式中,持续集成结合自动化测试,理论上只要测试用例足够,任何因代码改动导致的关联问题都能在代码提交当天被发现,而且无需人工介入,所以除了对研发的设计、编码带来巨大改善,测试部门更是TDD的最大受益对象。来源:https://www.wubayue.com

3 测试驱动开发的前置条件

TDD在项目中的实施具有一定门槛,我认为以下几个前置条件缺一不可:

敏捷开发模式

TDD伴随敏捷开发而生,因此敏捷开发是实施TDD的最基础条件。

持续集成平台

持续集成与敏捷开发相辅相成,它为快速迭代、代码质量、团队协作提供支撑。来源:https://www.wubayue.com

自动化测试平台

要大范围、高频率的随时验证研发人员提交的代码,只有通过自动化测试方案。自动化测试除了测试部门的资源投入建设,还需要研发部门的协同配合,比如研发提供业务逻辑API。

4 测试驱动开发的实施步骤

测试驱动开发流程示意图

4.1 产品需求

PO(Product Owner,产品负责人)提供清晰的产品需求规格/用户故事,这是项目的起点,也是必需项,如果缺失的话会为后续工作带来诸多阻碍,因此项目中即使没有专职PO,也应设有PO角色,PO为后续的软件设计、开发、测试提供标的,就像是茫茫大海中的导航明灯,为测试和研发工作解决争端、指引方向。

4.2 软件设计

软件设计是TDD实施步骤中非常重要的一环,它以测试方案、测试用例的设计作为主导,研发的接口设计围绕测试用例展开,但同时研发的实施方案也会影响测试用例,因此测试与研发在设计阶段需要频繁沟通,且相互依赖。

在测试部门,需要具有丰富经验的TSE(Test System Engineer,测试系统工程师)来主导测试方案,然后由测试工程师完善测试用例。同样在研发部门,需要经验丰富的SE(System Engineer,系统工程师,通常也是软件架构师)主导软件设计,然后由开发工程师编码实现。让TSE与SE参与设计至关重要,他们不是以顾问角色为项目提供指导,而是应直接对设计结果负责,这样最大限度保障了设计质量,减少编码阶段的反复,如果在编码阶段发现设计上的问题,其补救成本和代价是非常高昂的。

在设计阶段,测试部门会针对业务场景输出测试用例,研发的主要工作则是针对测试用例设计API接口,这些API接口既是核心业务逻辑的构成单元,同时也需要提供给自动化测试调用。软件架构师在与测试人员的反复沟通中确认这些接口能满足每一条测试用例,并将它们精确到类中方法与参数的签名。

4.3 软件开发

在传统模式中,软件只有完成整个功能的编码后才能进行测试,但在TDD模式下,软件开发与自动化测试用例开发是同步进行的,借助持续集成平台与自动化测试,在代码提交的同时,就可以对提交的功能进行测试,无需人工介入,即时反馈。

当前自动化测试用例的主流开发语言毫无争议的是Python,而研发端业务代码的开发语言则各不相同,测试部门的Python如何调用各种语言开发出来的业务API呢?可以自定义通信协议,比如基于HTTP、基于TCP/IP都行,但更好的方案是使用现有的成熟框架,比如RPC(Remote Procedure Call,远程过程调用,调用远程函数就像本地一样丝滑)框架。各大厂都有自己的RPC框架,像Google的gRPC,Facebook的Thrift,阿里的Dubbo等,目前不论从市场占有率还是综合性能表现来看,gRPC都是王者,想了解gRPC技术实施细节的同学可参考我之前的几篇文章:《gRPC基础:C++服务端与客户端代码示例》《gRPC进阶:通过stream实现观察者模式》《gRPC基础:C#服务端与客户端代码示例》

5 UTDD简介

UTDD红绿蓝规则

前面章节介绍的ATDD只是将测试左移,让测试提前介入并作为核心驱动力,在实施方法上本质并没有特别大的变化,我认为它是相对温和并且容易实施的测试驱动开发方案。而UTDD(单元测试驱动开发)则激进了很多,它要求研发的单元测试用例驱动业务代码实现,我们看一下UTDD中著名的红绿蓝规则:红色表示先编写一部分测试用例,此时因为还没有与之对应的业务代码,所以测试用例会执行失败;绿色表示补充业务代码,使测试用例能够执行成功;蓝色表示在业务代码被测试用例覆盖后,可大胆的进行重构,周而复始直至开发完成。这套规则乍一看好像没什么特别,但关键在于第一步红色部分,需要在业务编码之前先编写测试用例,也就是业务逻辑实现是基于测试用例一步一步来驱动的。这与常规在业务代码中考虑并实现业务逻辑截然不同,开发人员需要从测试视角来逐步实现业务逻辑。虽然这有诸多好处,但我想这样巨大的转变需要考虑团队的适应能力。

我们的项目首次在敏捷开发中引入ATDD,这个变化对整个团队来说已足够大了,因此现阶段并不计划让开发团队再将编码方式由传统模式转变为UTDD模式,后续如果有机会实施了UTDD,再向大家分享这部分内容。

6 结束语

前后大概花费了一年时间,从测试驱动开发的理论研究,到最终项目中的落地实施,还是取得了非常不错的效果。我想像汇川这样的硬件驱动型公司,在敏捷开发的基础上已更进一步大规模推广TDD,互联网和软件公司,更应该积级尝试这种更高效的软件开发模式。来源:https://www.wubayue.com

<全文完>

赞同 0
反对 0
登录注册会员 后发表评论。
评论列表