资讯专栏INFORMATION COLUMN

BDD:Behavior-Driven Development 行为驱动开发

philadelphia / 2479人阅读

摘要:理想情况下项目的参与人员能根据当前系统行为列表判断新加入的功能行为是否会破坏现有功能。通过暂时挂起不实现具体行为,你可以进行测试优先的开发。

我们一般将测试放在项目的最后时刻进行,甚至在时间较紧时、预算超支,或者其他原因发生时会放弃测试。

项目的管理者好奇为什么开发者就是不能一开始就明白(需求、设计),而在系统有很多利益相关者并且不同的相关者对系统有不同的看法的时候,开发者(特别是在大型项目中),更容易变得迷糊,使得协商过程像盲人摸象一样。

每个项目的开始,必然是有一个关于项目行为表现、功能特点的讨论会,由客户或者其他业务人员向开发团队解释他们就行想要什么。(苦逼又令人讨厌的策划....)

有时候,这些交互、讨论以敏捷开发形式表现;有时是设计文档,就像去年查理斯-福克斯的博客所说的那样;有时是由Keynote制作的流程图或者模型;有时甚至是一个简单的电话解释而已。

仅通过这些沟通,开发者一般只是负责构建一个能够运行的系统而已,而这对于一个开发团队来说,是远远不够的。这(单纯的沟通)对于大型系统的业余开发人员来说尤其困难。

为什么不进行测试?

一般存在一个争议:如果客户/业务人员一开始就对系统的行为、特征有充分的认知,那么为什么往往要撤销对这些功能、行为进行测试?

答案可能非常简单:测试一般被认为是共享资产(对大家都有用的),也不被认为是对项目开发有实际价值的。测试只对工程师有用或者只对特定的一些人有用。

那么如何才能使得测试对大家都有价值呢?仅仅是列出系统的功能特性吗?当然不是,我们应该使用behavior-driven development (BDD)而不是仅仅是test-driven development (TDD)。

BDD是什么?

行为驱动开发应该着眼于你的代码所要实现的业务行为,即“为什么要编写这样的代码?”它可以很好的支撑项目核心工作流程,特别是对于交叉功能的了解与实现。

敏捷BDD开发有很大的好处。当开发者和敏捷项目主或者业务分析师坐在一起,将大概功能框框(具体如何实现由开发者在框内填写)写在白板上:

业务人员指定系统的行为特性

开发者基于他们自己对系统的理解向业务人员提问,同时从开发者角度写下其他附加的行为。

理想情况下:项目的参与人员能根据当前系统行为列表判断新加入的功能行为是否会破坏现有功能。

我发现这些简单的行为给了我一些约束,使得我能像一个开发者一样思考:这些我已经实现的这些测试能够将我的实现代码约束在一个规范之中。而那些功能代码只需满足这些约束、规范,就能在协作开发中快速完成。

这种协作方法使得我更加专注于提供给最终用户的功能特性,而且业务人员可以在旁约束、纠正我对系统行为的理解,而不是系统的具体实现。这就是BDD和TDD的突出区别。

BDD的一个例子

情景:你是负责开发企业会计系统的团队一员,系统使用Rails框架实现。有一天,业务人员问你一个关于提醒模块的功能:提醒用户他们正在等待处理的发票。你坐下来和业务人员定义这个功能模块。

你打开你的文本编辑器/笔记本,开始在上面画上框框,每个框代表用户需要的功能行为:

//为每个新支票添加一个提醒日期
it "adds a reminder date when an invoice is created"
//当提醒日期到来就发邮件提醒
it "sends an email to the invoice"s account"s primary contact after the reminder date has passed"
//如果用户阅读了邮件就给用户打上标记
it "marks that the user has read the email in the invoice"

在开发中专注于系统行为使得测试在验证你所实现系统行为是否正确中是十分有用的,而不仅仅是编码正确(没有bug)。要注意的是,这种分析要用业务语言而不是实现系统所采用的具体开发语言。
你不需要将“发票属于哪个用户”描述出来,因为开发团队之外的人也并不关心这种关系。

有些开发者在讨论/开发现场就写出测试样例,在系统中调用这些所要测试的方法,设置期望值,如下:

it "adds a reminder date when an invoice is created"  do
  current_invoice = create :invoice
  current_invoice.reminder_date.should == 20.days.from_now
end

这些测试样例必然是运行失败的,因为我们还没有实现设置remind_date的代码。

失败的测试

我明白开发者为什么会写失败样例测试,但是从业务人员的角度来说,这写测试对他并无用处。一些业务人员可能会被这些测试细节、实现细节搞迷糊,甚至我学得一些开发知识后就插手开发人员的工作。(数据库设计、代码复用)

从我的经验来开,如果开发者对于特定系统行为写出多行实现概要,业务人员会感到不耐烦,他们会觉得这是浪费他们的时间并不耐烦的急于阐释他们假想的下一个系统行为。

BDD和TDD的区别

现在我们从另一个角度看:使用TDD方法,并且写出测试概要:

//创建支票后,过期日期=创建日期+20天
it "after_create an Invoice sets a reminder date to be creation + 20 business days"
//Account#primary_payment_contact返回支付联系人或者用户项目管理者
it "Account#primary_payment_contact returns the current payment contact or the client project manager"
//InvoiceChecker#mailer 检查是否过期,如果是,就发邮件提醒。
it "InvoiceChecker#mailer finds invoices that are overdue and sends the email"

这些测试是有用的,不过只是对一些人有用:工程师。BDD是用来沟通(交叉功能)项目成员的工具,包括开发者和业务人员。

通过暂时挂起(不实现)具体行为,你可以进行测试优先的开发。首先,编写测试;接着,运行测试(当然是运行失败的,因为我们都还没开始实现具体行为);编写行为,使他能跑;修正,使他能正确运行。

BDD社区的很多工作和产品都使得测试中的断言检查读起来向普通语言一样。下面是一个刻板老套的RSpec测试:

a = 42
a.should == 42

这个格式使得结果易于阅读和理解。但注意这不是我们在此所应该做的,我们应该尽快获取系统行为的准确描述,并且坚持“每个系统行为都要测试”的原则。而从之前白板上画框框的工作,我们基本能够知道系统行为是什么。

BDD不是修正编码的奇特方式,它只是用来让团队成员(包含业务人员、顾客)对系统行为进行沟通而已。

BDD是关于协作和沟通的

再回看刚才的例子:企业会计系统。

你和业务人员讨论项目的功能:你(开发者)从内部(各模块是如何协作的)分析系统,而他们(业务人员)就从外部分析。

你会思考一些情况并且并对系统分析师(业务人员)就一下情况进行提问:

//默认的提醒日期是?在支票到期前的第几天提醒?
* What"s the default reminder date going to be? How many days before the invoice due date?
//这些天数是指自然日还是工作日?
* Are those business days or just calendar days?
//如果这些支票所属的账号主没有对应的联系方式,那该怎么办?
* What happens if there"s not a primary contact associated with the account?

因此,使得业务人员理解你的问题是非常重要的,因为他们可能对具体开发缺乏相应的知识。

有时候,BDD是一种有益于两个部门(如策划和开发)协作和沟通的工具,也是清晰划分系统功能界限和对开发团队(如预计开发时间)有更好估计的一种方法。
可能你意识到无法从给定日期计算10天以后的日期(因为每个月的天数都不一样),那么你就需要实现这个计数功能,而业务人员对这个计数功能可能并不关心。

开发者有对于具体开发的思考(比如你说的‘天’是什么),业务人员也有他们的思考(如:请不要使用’过期’这个词语了,有时候它有不同的意思)。因此,只由一方来考虑系统功能和测试就会抹杀掉另一方的有价值的观点。

当然如果业务人员或者客户不能和开发者共处一室的时候,让他们将期望的系统行为和开发者自己的分析、理解写在纸上也是一个有效的沟通方法。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/8765.html

相关文章

  • 学会JavaScript测试你就是同行中最亮的仔(妹)

    摘要:测试驱动开发是一种使用自动化单元测试来推动软件设计并强制依赖关系解耦的技术。使用这种做法的结果是一套全面的单元测试,可随时运行,以提供软件可以正常工作的反馈。 showImg(http://ws1.sinaimg.cn/large/005NRne3gy1g2cmxxl7c5j30nc0c8h1p.jpg); 一、几种概念(稍微了解一下) ATDD: Acceptance Test Dr...

    fengxiuping 评论0 收藏0
  • 探知JS测试(1)

    摘要:单元测试这是测试类型的一种,所谓的单元即,由一些函数组成能完成某项功能的模块。单元测试的过程想好测试用例动手写测试查看测试结果,通过则否则应该进行测试模式想说一下,测试模式和单元测试的区别。测试模式包括单元测试通常测试模式有和模式。 有一定水平的js童鞋,应该会经常看到一些书上,在介绍项目的时候,会不由自主说道测试。 比如,单元测试,函数测试,或是TDD,BDD等测试模式。没错,这也是...

    xingpingz 评论0 收藏0
  • 探知JS测试(1)

    摘要:单元测试这是测试类型的一种,所谓的单元即,由一些函数组成能完成某项功能的模块。单元测试的过程想好测试用例动手写测试查看测试结果,通过则否则应该进行测试模式想说一下,测试模式和单元测试的区别。测试模式包括单元测试通常测试模式有和模式。 有一定水平的js童鞋,应该会经常看到一些书上,在介绍项目的时候,会不由自主说道测试。 比如,单元测试,函数测试,或是TDD,BDD等测试模式。没错,这也是...

    bladefury 评论0 收藏0
  • TDD,BDD

    摘要:每个阶段就能进行测试,节省开发成本。最初是由在年命名,它包括验收测试和客户测试驱动等的极限编程的实践,作为对测试驱动开发的回应。的优点是将各个参与协作团队的人员跨领域集中在一起达成一致的理解,节约了很多协作上的沟通时间。 TDD(测试驱动开发 Test Driven Development) TDD(Test-Driven Development) 测试驱动开发 是敏捷开发中的一项核心...

    shadajin 评论0 收藏0
  • 前端单元测试探索

    摘要:单元测试的首要目的不是为了能够编写出大覆盖率的全部通过的测试代码,而是需要从使用者调用者的角度出发,尝试函数逻辑的各种可能性,进而辅助性增强代码质量测试是手段而不是目的。 本文已发布在稀土掘金 转载请注明原文链接:https://github.com/ecmadao/Co... 虽然很多公司有自己的测试部门,而且前端开发大多不涉及测试环节,但鉴于目前前端领域的快速发展,其涉及面越来...

    陈江龙 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<