导航
1前言
2为何要使用测试驱动开发?
3测试驱动开发的前置条件
4测试驱动开发的实施步骤
4.1软件设计
4.2软件开发
4.3软件测试
4.4继续集成
5结束语
1 前言
测试驱动开发(TDD:Test Driven Development)是敏捷开发中的一项核心实践,推崇通过测试来驱动整个开发的进行。TDD有别于传统“先编码,后测试”的开发过程,而是要求在编写业务代码之前,先编写测试用例。TDD的概念大致在上世纪90年代随着极限编程(XP:Extreme Programming)提出,但在敏捷开发已大行其道的今天,TDD仍未普及,对其也是褒贬不一,存在一定争议。
我将TDD理解为广义TDD和狭义TDD两个概念,所谓广义TDD,更贴近ATDD(Acceptance Test Driven Development,验收测试驱动开发),指的是在编码之前先明确验收标准(AC:Acceptance Criteira),然后功能开发围绕验收标准展开,它符合我们的常识,一件事情在实施之前先明确要达成的目标。而狭义TDD,指的是软件开发阶段的UTDD(Unit Test Driven Development),即单元测试驱动开发,它有一个著名的红绿蓝规则,大致思想是通过测试用例来驱动业务逻辑编码,这与常规的编码有很大不同,当然它也有独道的优势。
本文主要根据实际项目中的痛点,围绕广义ATDD的思想,探讨如何通过测试前置解决项目中存在的问题。
2 为何要使用测试驱动开发?
驱使我在项目中实施TDD的主要原因来自项目中的痛点,但每个项目团队遇到的问题各不相同,因此本文仅作参考,如有雷同欢迎对号入座共同探讨。先简单介绍一下我们的情况,项目团队大概30人左右,采用敏捷开发模式,开发人员有单元测试基础,有相对完善的持续集成环境,有自动化测试条件。
架构设计师
在架构设计环节,我们是由SEG(System Engineer Group,系统工程师小组)来完成需求、用户故事以及软件概要设计。但我们的SEG大多不是软件专业出生,软件架构设计能力参差不齐,所以很多时候软件设计质量就无法得到保障,甚至风格无法统一,更无法对软件设计质量进行量化。我的想法是,首先构建项目架构的基础约束,比如基于DDD(领域驱动设计)的业务层级组织、一定的抽象、松散耦合策略等,然后把SEG们集中起来培训学习,让他们理解项目中的设计模式和方法。最后借助TDD,将每个功能拆解到业务抽象层的类和方法签名。比如某项功能由哪些类哪些方法构成,这些方法的入参出参是怎样的,SEG设计输出到这个颗粒度然后再进行评审。这样在架构设计环节,就得到了相对可靠,具备一致性,以及可量化的设计。在当前项目背景条件下,TDD能帮助我们解决这些软件设计过程中存在的问题。
开发工程师
项目中的开发工程师一部分为应届毕业生,但即使是有经验的开发工程师,代码质量也参差不齐。问题主要出在架构设计对编码的约束力太弱,开发工程师只关注实现功能,有时间时参考一下设计,时间紧张时就直接上代码,编码和设计脱钩。借助TDD,我想可以改观这种情况,设计不再是一份仅供参考的文档,而是强制渗透到代码层面,开发工程师要做的事情是对每个抽象接口进行具体编码实现,这样使设计和编码两个环节无缝衔接,也符合DDD(Domain Driven Design,领域驱动设计)在代码中传承业务知识的理念。
编码环节的另一个问题是代码质量,这是UTDD想解决的核心问题,也涉及了比较广的范围,比如开发人员对业务逻辑的思考抽象能力、对边界值的考虑、对代码变更影响点的评估等等。在后续的实施步骤章节中会展开描述UTDD的红绿蓝思想,但在当前我们并不考虑实施UTDD,当前的主要目标是在开发中更深入的推广单元测试。
在项目实施过程中,还有一个令开发工程师和测试工程师都很头痛的“功能自测”环节:即测试工程师为了确保每个提测版本的质量和测试效率,要求开发工程师在提测前,先自已对功能进行测试。这是一个合理的要求,但在实际执行时面临诸多问题,比如自测的质量取决于开发工程师是否细心,也取决于开发工程师的闲忙,闲时认真负责,忙时囫囵吞枣。我想通过TDD,完全可以取代掉功能自测这个环节。
测试工程师
当前阶段的测试仍以人工手动测试为主,人力资源消耗大且稳定性不高,希望借助TDD,全面转变为自动化测试为主,手动测试为辅。
另一个问题是开发人员时常改动代码,但未能准确评估输出改动影响点,导致人工漏测并出现BUG。在TDD模式中,自动化测试与单元测试应集成至每日构建,这样代码改动的当天就能识别因改动产生的问题。
3 测试驱动开发的前置条件
TDD在项目中的实施具有一定门槛,我认为以下几个前置条件缺一不可:
3.1 敏捷开发模式
TDD伴随敏捷开发而生,因此敏捷开发是实施TDD的最基础条件。
3.2 白盒测试能力
这其中包括项目代码的可测试性,以及开发团队的单元测试经验。
3.3 自动化测试平台
验收标准的判定由两部分构成:在研发端主要以白盒方式的单元测试,以及在测试端主要以黑盒方式的自动化测试,单元测试关注代码逻辑细节,自动化测试关注业务场景,两者互为补充。自动化测试需要测试部门的资源投入建设,还需要研发的协同配合,比如开发提供业务逻辑API。
3.4 持续集成平台
持续集成是敏捷开发的最佳拍档,它为快速迭代、代码质量、团队协作提供支撑。
4 测试驱动开发的实施步骤
此处再次声明,本文关于TDD的理解与实施步骤均为笔者根据自己项目中的情况适配,仅供参考,请勿当作TDD标准解读。
4.1 软件设计
软件设计是实施步骤中最重要的一环,它对SEG有着较高要求,除了将产品需求拆解为用户故事外,还需要完成如下两个重要事项:
一是对开发工程师输出用户故事对应的抽象类,颗粒度至方法级别。即一个用户故事由哪些类构成,每个类中包含哪些方法,每个方法的入参出参是怎样的。这样除了获得相对更好的设计质量,还得到了更高的一致性,将后续开发工程师的工作简化为对一系列的抽象类的实现。由SEG提供的抽象方法同时还是单元测试用例的创建来源。
二是对测试工程师输出用户故事验收标准(AC:Acceptance Criteira),AC是敏捷开发中的一个标准交付件,它从业务场景的维度描述如何验收开发出来的用户故事,自动化测试用例均来自于AC。
4.2 软件开发
在UTDD的软件编码环节,一直存在一些质疑和争议,我们先看一下UTDD中著名的红绿蓝规则:红色表示先编写一部分测试用例,此时因为还没有与之对应的业务代码,所以测试用例会执行失败;绿色表示补充业务代码,使测试用例能够执行成功;蓝色表示在业务代码被测试用例覆盖后,可大胆的进行重构,周而复始直至开发完成。这套规则乍一看好像没什么特别,但关键在于第一步红色部分,需要在业务编码之前先编写测试用例,也就是业务逻辑实现是基于测试用例一步一步来驱动的。这与常规在业务代码中考虑并实现业务逻辑截然不同,开发人员需要从测试视角来逐步实现业务逻辑。虽然这有诸多好处,但我想这样巨大的转变需要考虑团队的适应能力。
我们的项目首次在敏捷开发中引入ATDD,这个变化对整个团队来说已足够大了,因此现阶段并不计划让开发团队再将编码方式由传统模式转变为UTDD模式。回顾之前项目中的痛点,基本已得到了解决:开发人员的编码完全贯彻了设计,代码质量和一致性得到极大提升;如果单元测试执行的彻底,它会守护代码的变更迭代;之前依赖于研发人员主观因素的功能自测,通过针对代码逻辑的单元测试与针对业务场景的自动化测试完全可以将其取代。
4.3 软件测试
一直以来,软件测试都在向自动化的方向发展,因为测试活动本身具有极大的重复性,它更适合机器而不是人来完成。特别是随着Python这样简单编程语言的出现,随着人工智能的发展,以后的软件测试工作就是开发工作,开发人员一次编写自动化用例,机器多次自动执行即可。
在我们的项目中,通过TDD贯穿了设计、开发、测试环节:首先SEG在软件设计环节输出业务抽象接口与功能验收标准。然后开发人员依据业务抽象接口完成具体功能实现,自动化测试人员依据功能验收标准编写验收测试用例,两者同步进行,过程中协作联动。最后自动化测试通过持续集成平台部署,把控每日构建、版本提测等需要的项目关键环节。
4.4 持续集成
持续集成(CI:Continuous Integration)也是敏捷开发思想的产物:即然希望快速开发,那么同样希望快速的测试,快速的获得软件版本。持续集成以及这些年流行的DevOps,是涉及开发、测试、运维三者之间关系的另一个主题,此处不作展开。
总之在TDD中,单元测试与自动化测试,都需要借助持续集成平台,高效快速的对开发的功能进行验证。当然验证过程可根据项目情况灵活配置,比如冒烟测用于保障软件版本的基本可用,如果新开发的功能属于基础功能,那么它对应的自动化测试就应该纳入冒烟测试范畴,在每日构建中执行。
5 结束语
测试驱动开发是敏捷开发中并未大范围普及且具有挑战性的实施方案,它对资源、团队、能力均有较高的要求。本文根据实际项目中的痛点,结合TDD的思想方法,旨在改善项目流程,解决项目中的实际问题。
<全文完>