深度解析 - 全方位对比TDD和BDD
TDD
基本定义
TDD是测试驱动开发(Test-Driven Development)的英文简称,是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD虽是敏捷方法的核心实践,但不只适用于XP(Extreme Programming),同样可以适用于其他开发方法和过程。
两个衍生的概念
ATDD,Acceptance Test Driven Development,即验收测试驱动开发。在业务层次,进行需求分析时就要确定需求(如用户故事)的验收标准。
UTDD,Unit Test Driven Development,即单元测试驱动开发。在代码层次,在编码之前就要确定功能的验收脚本和验收标准。
基本原则
测试隔离:不同代码的测试应该相互隔离。对一块代码的测试只考虑此代码的测试,不要考虑其实现细节(比如它使用了其他类的边界条件)。
一顶帽子:开发人员开发过程中要做不同的工作,比如:编写测试代码、开发功能代码、对代码重构等。做不同的事,承担不同的角色。开发人员完成对应的工作时应该保持注意力集中在当前工作上,而不要过多的考虑其他方面的细节,保证头上只有一顶帽子。避免考虑无关细节过多,无谓地增加复杂度。
测试列表:需要测试的功能点很多。应该在任何阶段想添加功能需求问题时,把相关功能点加到测试列表中,然后继续手头工作。然后不断的完成对应的测试用例、功能代码、重构。一是避免疏漏,也避免干扰当前进行的工作。
测试驱动:如果需要完成某个功能或者某个类,首先编写测试代码,考虑其如何使用、如何测试,然后在对其进行设计、编码,并要保证通过测试代码的测试。
先写断言:测试代码编写时,应该首先编写对功能代码的判断用的断言语句,然后编写相应的辅助语句。
可测试性:功能代码设计、开发时应该具有较强的可测试性。其实遵循比较好的设计原则的代码都具备较好的测试性。比如比较高的内聚性,尽量依赖于接口等。
及时重构:无论是功能代码还是测试代码,对结构不合理,重复的代码等情况,在测试通过后,及时进行重构。
小步前进:软件开发是个复杂性非常高的工作,开发过程中要考虑很多东西,包括代码的正确性、可扩展性、性能等等,很多问题都是因为复杂性太大导致的。极限编程提出了一个非常好的思路就是小步前进。把所有的规模大、复杂性高的工作,分解成小的任务来完成。对于一个类来说,一个功能一个功能的完成,如果太困难就再分解。每个功能的完成就走测试代码-功能代码-测试-重构的循环。通过分解降低整个系统开发的复杂性。这样的效果非常明显。几个小的功能代码完成后,大的功能代码几乎是不用调试就可以通过。一个个类方法的实现,很快就看到整个类很快就完成啦。本来感觉很多特性需要增加,很快就会看到没有几个啦。你甚至会为这个速度感到震惊。
基本流程
戴明博士最早提出了PDCA循环的概念,所以又称其为”戴明环”。
PDCA循环是能使任何一项活动有效进行的一种合乎逻辑的工作程序,特别是在质量管理中得到了广泛的应用。P、D、C、A四个英文字母所代表的意义如下:
P(Plan) –计划。包括方针和目标的确定以及活动计划的制定;
D(Do) –执行。执行就是具体运作,实现计划中的内容;
C(Check) –检查。就是要总结执行计划的结果,分清哪些对了,哪些错了,明确效果,找出问题;
A(Action)–行动(或处理)。
对总结检查的结果进行处理,成功的经验加以肯定,并予以标准化,或制定作业指导书,便于以后工作时遵循;对于失败的教训也要总结,以免重现。对于没有解决的问题,应提给下一个PDCA循环中去解决。
优缺点
优点:
- 在任意一个开发节点都可以拿出一个可以使用;
- 节省调试时间和找问题时间;
- 含少量bug并具一定功能;
- 帮助获得更快的反馈;
- 促进产生更简洁、更好的设计;
- 提高开发人员工作效率;
- 允许任何团队成员在没有某一团队成员的情况下修改其代码;
- 使开发人员有信心轻松更改大型框架的应用程序;
- 使代码更易扩展和维护。
缺点:
- 增加代码量;
- 增加研发人员的工作量;
- 研发人员会有比较强的抵触心理;
- 开发完成的产品可能会与实际不符。
BDD
基本定义
BDD是行为驱动开发(Behavior-Driven Development)的英文简称,是一种敏捷软件开发的技术,也是一种设计方法论。它是测试驱动开发(ATDD)的延伸,更侧重于关注设计过程,其借鉴了敏捷和精益实践,让敏捷研发团队尽可能理解产品经理或业务人员的产品需求,并在软件研发过程中及时反馈和演示软件产品的研发状态,让产品经理或业务人员根据获得的产品研发信息及时对软件产品特性进行调整。BDD帮助敏捷研发团队把精力集中在识别、理解和构建跟业务目标有关的产品特性上面,并让敏捷研发团队能够确保识别出的产品特性能够被正确设计和实现出来。
BDD是第二代的、由外及内的、基于拉(pull)的、多方利益相关者的(stakeholder)、多种可扩展的、高自动化的敏捷方法。它描述了一个交互循环,可以具有带有良好定义的输出(即工作中交付的结果),即已测试过的软件。
BDD的重点是通过与利益相关者的讨论取得对预期的软件行为的清醒认识。它通过用自然语言书写非程序员可读的测试用例扩展了测试驱动开发方法。行为驱动开发人员使用混合了领域中统一的语言的母语语言来描述他们的代码的目的。这让开发者得以把精力集中在代码应该怎么写,而不是技术细节上,而且也最大程度的减少了将代码编写者的技术语言与商业客户、用户、利益相关者、项目管理者等的领域语言之间来回翻译的代价。
基本原则
目标:确立不同利益相关者要实现的远景目标。要将用户、业务人员(项目经理、产品经理)、研发人员、测试人员对要实现的业务功能达成共识。
特性:使用特性注入方法绘制出达到这些目标所需要的特性。
方法:通过由外及内的软件开发方法,把涉及到的利益相关者融入到实现的过程中。
描述:使用例子来描述应用程序的行为或代码的每个单元。使用“应当(should)”来描述软件的行为,以帮助阐明代码的职责,以及回答对该软件的功能性的质疑,使用“确保(ensure)”来描述软件的职责,以把代码本身的效用与其他单元(element)代码带来的边际效用中区分出来。
效率:通过自动运行这些例子,提供快速反馈,进行回归测试。
BDD工具对比
BDD基本流程
BDD软件研发过程
1. 产品经理(业务人员)通过具体的用户故事使用场景来告诉软件需求分析人员他(她)想要什么样的软件产品。使用软件产品的使用场景来描述软件需求可以尽可能的避免相关人员错误理解软件需求或增加自己的主观想象的需求。
2. 软件需求分析人员(BA)和研发团队(研发人员、测试人员)一起对产品经理(业务人员)的用户故事进行分析,并梳理出具体的软件产品使用场景举例,这些场景举例使用结构化的关键字自然语言进行描述,例如中文、英文等。
3. 研发团队使用BDD工具把用户故事场景文件转化为可执行的自动化测试代码,研发人员运行自动化测试用例来验证开发出来的软件产品是否符合用户故事场景的验收要求。
4. 测试人员可以根据自动化测试结果开展手工测试和探索性测试。
5. 产品经理(业务人员)可以实时查看软件研发团队的自动化测试结果和BDD工具生成的测试报告,确保软件实现符合产品经理(业务人员)的软件期望。
BDD如何使用呢?
第一步:提出战略目标
童鞋们,市场竞争越来越激烈,飞机票代订业务也越来越难做。董事会要求我们今年的营业额要比去年增加20%,成本要降低10%.今天召集大家开会的目的就是请市场部、销售部和研发的童鞋一起献计献策,群策群力完成今年的业务目标。
第二步:业务部门提出具体业务目标和软件需求
1. 增加常旅客积分积累和使用功能,通过我们公司电子商务网站或相关渠道购买机票的旅客可以积累里程积分。这些积分可以在下次购买机票时使用积分购买机票,也可以设置积分共享,让他(她)的亲友使用自己的积分购买机票;
2. 增加电子商务网站访问渠道,例如可以通过微信或支付宝平台查询机票,购买机票,查看和共享里程积分;
3. 为了增加客户的黏着度,新增用户登录积分和信息共享积分,用户登录使用我们电子商务网站越多,积分也越多。用户在我们电子商务网站发表旅行心得或为他人提供帮组信息也可以获取积分。使用积分可以这换成里程积分兑换机票。
第三步:用户故事梳理、业务场景梳理
用户故事场景:新会员注册后初始状态为铜牌会员
# language: zh-CN
场景: 新会员注册后初始状态为铜牌会员
假如: 小明童鞋还不是一位常旅客会员
当: 小明童鞋注册常旅客会员,注册时输入信息:
|用户名|密码|Email|
|:-:|:-:|:-|:-:|
|xiaoming|12345678|xiaoming@qatools.cn|
那么: 注册成功后,小明童鞋的初始会员级别为'铜牌会员
第四步:基于BDD工具,实现验收场景
@假如("^小明童鞋还不是一位常旅客会员$")
public void 小明童鞋还不是一位常旅客会员() throws Throwable {
//TODO: 注册前小明童鞋还不是常旅客会员
throw new PendingException();
}
@当("^小明童鞋注册常旅客会员:$")
public void 小明童鞋注册常旅客会员(DataTable arg1) throws Throwable {
///TODO:小明童鞋注册为会员
throw new PendingException();
}
@那么("^注册成功后,小明童鞋的初始会员级别为'铜牌会员'$")
public void 注册成功后_小明童鞋的初始会员级别为_铜牌会员() throws Throwable {
//TODO: 注册成功后检查小明童鞋的会员状态
throw new PendingException();
}
第五步:基于业务场景,实现功能
public class WhenCheckingMinimumStatusPoints {
FrequentFlyer member;
@Before
public void newFrequentFlyer() {
member = FrequentFlyer.newMemberWithUsername("xiaoming").password("12345678").email("xiaoming@toolsqa.cn");
}
@Test
public void should_have_bronze_status_initially() {
assertThat(member.getStatus()).isEqualTo(FrequentFlyerStatus.BRONZE);
}
}
第六步:验收测试场景
public class UserRegisterPage extends PageObject {
@FindBy(name="username")
private WebElement username;
@FindBy(name="password")
private WebElement password;
@FindBy(name="email")
private WebElement email;
@FindBy(css = ".btn[value='Sign up']")
private WebElement signup;
public void signupAs(String username, String userPassword, String userEmail ) {
username.sendKeys(username);
password.sendKeys(userPassword);
email.sendKeys(userEmail);
//点击注册按钮
signup.click();
}
}
第七步:查看验收报告
BDD优点
- 通过使用非技术语言扩大了受众
- 从用户和开发人员的角度聚焦系统行为
- 经济且高效
- 减少了每次部署后回归的工作量
TDD和BDD的差异
TDD | BDD | |
---|---|---|
定义 | TDD是一项开发技术,关注点在功能的实现 | BDD是一项开发技术,关注点在系统的行为 |
参与者 | 开发者 | 开发者、用户、QAs |
使用语言 | 开发语言(Java、Python等等) | 小黄瓜(Gherkin) |
主要关注点 | 单元测试 | 理解需求 |
使用工具 | JDave, Cucumber, JBehave, Spec Flow, BeanSpec, Gherkin Concordian, FitNesse | Gherkin, Dave, Cucumber, JBehave, Spec Flow, BeanSpec, Concordian |