.wj_nav { display: inline-block; width: 100%; margin-bottom: 0.375rem }
.wj_nav span { display: inline-block; margin-right: 0.375rem; color: rgba(102, 102, 102, 1) }
.wj_nav a, .wj_nav a:link, .wj_nav a:visited { color: rgba(51, 51, 51, 1); text-decoration: underline }
.wj_nav a:hover { color: rgba(255, 102, 0, 1); text-decoration: none }
.wj_title_1 { display: inline-block; width: 100%; margin-bottom: 1rem; border-left: 0.375rem solid rgba(255, 102, 0, 1); background-color: rgba(232, 232, 232, 1); font-size: 1.5rem; padding: 0.3125rem 0.625rem }
.wj_title_2 { display: inline-block; width: 100%; font-size: 1.25rem; font-weight: bold; margin-bottom: 1rem }
.wj_title_3 { display: inline-block; width: 100%; font-size: 1rem; font-weight: bold; margin-bottom: 1rem }
.wj_cont { line-height: 180%; margin-bottom: 1rem; font-size: 1rem }
.wj_img { display: inline-block; width: 100%; margin-bottom: 1rem }
.wj_code { margin-top: 0 !important; margin-bottom: 1rem !important; font-size: 1rem }
.wj_table { border: 1px solid rgba(255, 136, 56, 1); border-collapse: collapse; margin-bottom: 1rem }
.wj_table tr { display: table-row; vertical-align: inherit }
.wj_table tr th { display: table-cell; vertical-align: inherit; font-weight: normal; border: 1px solid rgba(255, 136, 56, 1); padding: 5px 10px; background-color: rgba(255, 102, 0, 1); color: rgba(255, 255, 255, 1); text-align: left }
.wj_table tr td { display: table-cell; vertical-align: inherit; border: 1px solid rgba(255, 136, 56, 1); padding: 0.3125rem 0.625rem; background-color: rgba(255, 255, 255, 1); text-align: left }
.wj_link, .wj_link:link, .wj_link:visited { color: rgba(51, 51, 51, 1); text-decoration: underline }
.wj_link:hover, .wj_link:active { color: rgba(255, 102, 0, 1) }

导航

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的思想方法,旨在改善项目流程,解决项目中的实际问题。

<全文完>

测试驱动开发(TDD)浅析的更多相关文章

  1. 测试驱动开发(TDD)的思考

    极限编程 敏捷开发是一种思想,极限编程也是一种思想,它与敏捷开发某些目标是一致的.只是实现方式不同.测试驱动开发是极限编程的一部分. 1.极限编程这个思路的来源 Kent Beck先生最早在其极限编程 ...

  2. 测试驱动开发 TDD

    一.详解TDD 1.1.TDD概念 :Test Drived Develop 测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种方法论.TDD的原理是在开发功能代码之前,编写单元测试用例代码,测试 ...

  3. 测试驱动开发(TDD)及测试框架Mocha.js入门学习

    组里马上要转变开发模式,由传统的开发模式(Developer开发,QA测试),转变为尝试TDD(Test-driven development,测试驱动开发)的开发模型.由此将不存在QA的角色,或者仅 ...

  4. 原创翻译-测试驱动开发(TDD)

    测试驱动开发原则 翻译自<<Expert Python Programming>> 测试驱动开发是指首先编写包含所有测试软件特点的测试集,然后再去开发软件.也就是说,在编写软件 ...

  5. 44 | 测试先行:测试驱动开发(TDD)

  6. 浅谈测试驱动开发(TDD)

    测试驱动开发(TDD)是极限编程的重要特点,它以不断的测试推动代码的开发,既简化了代码,又保证了软件质量.本文从开发人员使用的角度,介绍了 TDD 优势.原理.过程.原则.测试技术.Tips 等方面. ...

  7. (转)浅谈测试驱动开发(TDD)

    测试驱动开发(TDD)是极限编程的重要特点,它以不断的测试推动代码的开发,既简化了代码,又保证了软件质量.本文从开发人员使用的角度,介绍了 TDD 优势.原理.过程.原则.测试技术.Tips 等方面. ...

  8. 敏捷开发 —— TDD(测试驱动开发)

    测试驱动开发 TDD(Test-Driven Development)是敏捷开发的一项核心实践,同时也是一种设计技术和方法. 既然是测试驱动,便是测试,测试用例先行: 首先编写好测试用例,期待值,实际 ...

  9. 测试驱动开发与Python

    最近在看一本书<Test-Driven Development with Python>,里面非常详细的介绍了如何一步一步通过测试驱动开发(TDD)的方式开发Web项目.刚好这本书中使用了 ...

  10. 基于Python的测试驱动开发实战

    近年来测试驱动开发(TDD)受到越来越多的关注.这是一个持续改进的过程,能从一开始就形成规范,帮助提高代码质量.这是切实可行的而非天马行空的. TDD的全过程是非常简单的.借助TDD,代码质量会得到提 ...

随机推荐

  1. Java中SPI机制原理解析

    使用SPI机制前后的代码变化 加载MySQL对JDBC的Driver接口实现 在未使用SPI机制之前,使用JDBC操作数据库的时候,一般会写如下的代码: // 通过这行代码手动加载MySql对Driv ...

  2. IM跨平台技术学习(十一):环信基于Electron打包Web IM桌面端的技术实践

    本文由环信技术黄飞鹏分享,原题"实战|如何利用 Electron 快速开发一个桌面端应用",本文进行了排版和内容优化等. 1.引言 早就听说利用Electron可以非常便捷的将网页 ...

  3. 《深入理解Mybatis原理》MyBatis配置解析过程

    配置解析主体方法 public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfi ...

  4. 前端学习openLayers配合vue3(获取矢量图的个数,省份的个数)

    矢量图层绘制了一个中国地图,我们获取一下矢量图层的个数 关键代码 map .getLayers()//获取所有图层 .item(1)//获取矢量图层 .getSource() .on("ch ...

  5. LCR 170. 交易逆序对的总数

    交易逆序对的总数 在股票交易中,如果前一天的股价高于后一天的股价,则可以认为存在一个「交易逆序对」.请设计一个程序,输入一段时间内的股票交易记录 record,返回其中存在的「交易逆序对」总数. 示例 ...

  6. cmake-2

    似懂非懂,一定要搞懂基础的,剩下的边做边学,从案例中入手. 有关cmake的介绍,请参考: 1.https://www.cnblogs.com/pam-sh/p/13885959.html 2.htt ...

  7. 深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用

    title: 深入探讨数据库索引类型:B-tree.Hash.GIN与GiST的对比与应用 date: 2025/1/26 updated: 2025/1/26 author: cmdragon ex ...

  8. 0511-FileWrite字符输出流和JDK7中try..finally新的特性

    package A10_IOStream; import java.io.*; /* java.io.Writer:字符输出流,是所有字符数出流的最顶层抽象父类 共性方法 void write(int ...

  9. flutter如何搭建android环境

    1.电脑上按安装sdk 首先配置Java的JDK 配好后,输入java 出现内容说明安装成功 然后在输入javac 出现内容说明jre安装成功 2.电脑上安装android Studio 安卓下载地址 ...

  10. Prometheus修改默认数据存储时间

    Prometheus修改默认数据存储时间 Prometheus 的数据存储时间是通过命令行参数 --storage.tsdb.retention.time 来设置的.这个参数指定了 Prometheu ...