醒醒吧少年,只用Cucumber不能帮助你BDD

转载:http://insights.thoughtworkers.org/bdd/

引言

在Ruby社区中,测试和BDD一直是被热议的话题,不管是单元测试、集成测试还是功能测试,你总能找到能帮助你的工具,Cucumber就是被广泛使用的工具之一。许多团队选择Cucumber的原因是“团队要BDD”,也就是行为驱动开发(Behavior Driven Development),难道用了Cucumber之后团队就真的BDD了么?

事情当然没这么简单了,BDD作为一种软件开发方法论,一定要理解其含义并且遵循特定的流程,工具只不过是起辅助作用而已。会切菜的不一定都是厨子,会写代码的不一定都是程序员。Cucumber的作者Aslak也在博客中提到

在BDD出现的9年后,依然有不少团队在使用BDD时出现问题……BDD依然经常被人误解成单纯的测试,或者是一个可以被下载的工具。

同时,Aslak也吐槽了Cucumber目前的处境

就在最近,Cucumber已经被下载了超过500万次,我很高兴它如此受欢迎,同时也为它被广泛的误用而感到失望……Cucumber有时依然被错误的当成自动化测试工具,而不是我当时创建的东西。

那么问题来了,怎样在日常项目中使用Cucumber呢?真的能在日常项目中进行BDD开发么?要回答这个问题,我们需要重新认识一下BDD。

BDD的提出

2003年,开发人员Dan North偶然间发现把测试的标题经过简单的文字处理可以更好表达代码蕴含的业务逻辑,比如下面这段代码,

public class CustomerLookupTest extends TestCase {
    testFindsCustomerById() {
        ...
    }
    testFailsForDuplicateCustomers() {
        ...
    }
}

当我们把测试方法中的test去掉,给单词加上空格,然后把他们组合在一起时,就会出现:

CustomerLookup
 - finds customer by id
 - fails for duplicate customers
 - ...

在Dan看来,这无疑是对CustomerLookup类的描述,并且是用测试内容来描述代码中类的行为。Dan发现他似乎找到了一种方式,可以在TDD的基础上,通过测试来表达代码的行为。在尝到甜头后,Dan写了JBehave,用一个更关注代码行为的工具来代替JUnit进行软件开发。经过一番折腾后,Dan觉得只描述类行为不过瘾,便开始把关注点从类扩展到整个软件,他和当时项目组的业务人员一起把需求转化成Given/When/Then的三段式,然后用JBehave写成测试来描述软件的某种行为。当测试完成后,开发人员才开始编码,一旦测试通过,那软件就完成了测试中描述的某种行为。在他看来,他把TDD升级了,因为他不再只关注于局部类的方法,而开始关注整个软件的行为。

通过这种方式,Dan成功的把需求转换成了软件的功能测试,先写功能测试再驱动出产品代码,保证软件行为正确性。其次,Dan强调在测试中要尽可能的使用业务词汇,保证团队成员对业务理解一致。于是,BDD就此诞生

BDD不只是自动化测试

在上面的故事中,“测试”这个词出现了很多次,你是不是已经认为BDD就是用功能测试驱动产品代码的开发流程呢?其实不然,功能测试只是一个结果而已,更重要的是和业务人员一起分析需求,沟通交流来产生测试的过程。用测试驱动出来的代码可以保证是正确的,但如何保证测试是正确的呢?答案就是人,通过业务,开发和测试一起参与生成的测试文档,不仅能保证软件功能上是正确的,还能保证团队成员对业务理解是一致的。在测试文档中,也应该尽量保证使用自然语言和业务词汇,减少非技术人员的学习成本。

在多年之后,Dan也终于给出了他对BDD的定义

BDD是第二代的、由外及内的、基于拉(Pull)的、多方利益相关者的(Stakeholder)、多种可扩展的、高自动化的敏捷方法。它描述了一个交互循环,可以具有带有良好定义的输出(即工作中交付的结果):已测试过的软件。

Cucumber的另一位作者Matt Wynne也给出了自己的定义

BDD的实践者们通过沟通交流,具体的示例和自动化测试帮助他们更好地探索、发现、定义并驱动出人们真正想用的软件。

从上述定义我们可以看出,BDD更强调流程和一系列实践,自动化测试只是其中一部分而已。

Cucumber到底怎么用

理解了BDD的精髓后,我们就不难找出正确的使用Cucumber的方式了。根据Cucumber的定义,它的核心就是Specification,其实就是文档化的需求。Specification是通过Requrement Workshop生成的,在Workshop中,业务、开发和测试一起分析需求,把需求用自然语言写成文档,然后再转换成Given/When/Then的Specification文件,这样便完成了BDD中最重要的一步--定义软件正确的行为。接着开发人员开始编码,完成相应需求,保证Specification文件运行通过,整个流程结束。

简单来说,Cucumber其实不是一个自动化测试工具,而是一个促进团队沟通合作的工具。但由于Cucumber无法确保上述流程真正的发生,有很多团队简化或者跳过了Workshop,直接开始写Specification文件,没有沟通就很难保证理解一致,Bug也许就在那时潜伏了下来。这样大家也就不难理解作者吐槽的“Cucumber被广泛的误用”,其实Cucumber只是一个沟通工具,它只是刚巧可以运行测试而已。

理想很丰满,现实很骨感

任何工具和实践都有优缺点,Cucumber也不例外。团队在开始尝试新的实践或者工具时,多多少少都会碰到一些问题,下面我们就来看看一些使用Cucumber的问题。

没有业务人员参与的Specification

要么业务人员没时间写Specification,交给其他人写,写完之后业务人员也没时间去审核。在这种情况下,很难保证Specification的业务正确性,一旦Specification出现问题,团队可能出现理解不一致、甚至做错需求的现象。反过来看,Specification文件由自然语言而不是代码组成,也能反映出对非技术人员参与的重视程度。然而现实情况很难保证业务、测试、开发有充足的时间进行Specification的讨论和编写,这也是导致业务人员逐渐脱离Specification的主要原因。

Specification关注实现细节而不是业务逻辑

Cucumber使用自然语言描述业务需求,然而不少团队都陷入到了实现细节中。比如

Scenario: Detect agent type based on contract number

Given I am on the ‘Find me‘ page
And I have entered a contract number
When I click ‘Continue‘ button
And a contact number match is found
Then the "Back" button will be displayed

上面的描述满篇是点击了哪个按钮,输入了什么内容,看完之后反而让人有点困惑,用户到底为什么要做这些,做了之后有什么价值。这样的Specification既不能满足团队成员对业务需求的了解,也会由于界面的细微改动运行失败。

Step的嵌套调用

Specification文件由Step组成,在Step中我们可以通过Ruby进行自动化的页面操作。有时我们会发现某些Specification会重复进行一系列的操作,这时我们就可以把重复的Step进行组合,创建出新的Step。比如这样

Given there is student Harry
And there is professor Snape
And student Harry joins class of professor Snape

# use 1 new step instead of 3
Given student Harry in class of professor Snape

那么这个新的Step该怎么实现呢?Cucumber支持在Step中调用Step,比如这样

Given /^student (.*) in class of professor (.*)/ do |student, professor|
    step "there is student #{student}"
    step "there is professor #{professor}"
    step "student #{student} joins class of professor #{professor}"
end

乍一看好像没什么问题,其实不然。Step使用正则表达式进行匹配,问题恰恰出在正则上。

首先,正则灵活性很大,你确定上面例子中step “there is student #{student}”一定会调用到你想要调用的Step么?你无法确定在运行时,是否会出现另一个Step “there is student come from China”来截胡。

其次,正则逆推难度很大,也就是说当你看到“^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0).(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$”时,你很难看出这是在匹配IP地址。所以当我们需要修改step时,很难确定有多少个step在依赖它,这也加大了维护成本。

最后,嵌套次数过多的Step也会导致代码复杂,难以理解。

Specification Report可读性不高

Specification除了是自动化测试的描述文件之外,更重要的是它是软件的“活文档”。有时我们需要通过“活文档”进行知识传递。Cucumber虽然提供生成Report的功能,但效果未免有些差强人意。比如下面

满篇绿色的Step,再加上Given/When/Then来捣乱,这样的Report只是运行结果而已,可读性很差,很难当成软件需求文档。究其原因,主要因为Cucumber Report的表现力差。

首先,它只支持纯文本,在这个“一图胜千言,无图无真相”的时代很难只通过文字来描述复杂业务,如果能在文档中加上图片,甚至一段视频,都会帮助我们更容易的理解复杂业务。比如像下面这样的。

其次,Cucumber Report关注的更多是Step,而不是软件需求。当我们想到软件文档或者手册时,我们脑海中想到的更多是像教科书一样的文档,内容之间有层级和关联关系,每个功能有重点和概要内容,更偏向自然语言,而不是简单的把Specification堆在一起,满篇的Given/When/Then。在现实情况下,这样的Cucumber Report也难免没有人愿意阅读了。

改进措施

遇到上面的问题不要怕,我们只要理解问题本质,找到对策解决它,依然可以帮助我们更好地完成任务。下面我们就来尝试解决一下上面提到的三个问题。

让业务人员写/审查Specification

对于上面的关注细节的例子,如果我们换一个思路,不去考虑UI之类的东西,就会得出更精炼的Specification。比如下面

Scenario: Customer has a tied agent policy
so last name is required

Given I have a "TiedAgent" policy
When I submit my policy number
Then I should be asked for my last name

这样的Specification不再关注按钮,而关注具体的业务需求,这样就把细节的UI操作推向了Cucumber Step的实现中。这样可以更直接的展现需求,避免细节内容的干扰。如果情况允许,我更支持让业务人员写Specification,或者最起码也要审查Specification文件。通常业务人员是团队中最不懂技术的,这反而是他们的优势,可以把Specification变得更加面向需求,更加通俗易懂。

Step实现代码的重用

我们可以通过重构Step实现代码来进行有效的重用,比如下面

Given /^there is student (.*)/ do |student|
    ModelFactory.create_user(student)
end

Given /^there is professor (.*)/ do |professor|
    ModelFactory.create_user(professor)
end

Given /^student (.*) joins class of professor (.*)/ do |student, professor|
    ModelFactory.join_class(student, professor)
end

通过重构,我们抽象出ModelFactory进行相关数据准备,然后就可以重用ModelFactory实现新的Step

Given /^student (.*) in class of professor (.*)/ do |student, professor|
    ModelFactory.create_user(student, professor)
    ModelFactory.join_class(student, professor)
end

重用代码而不是重用Step,这样不仅可以让Step的实现代码更加简洁,同时也避免了Step的嵌套调用。

扩展Cucumber生成高质量的文档

Cucumber虽然自带不少种格式的Report,但都不能称其为真正的文档。不过我们可以通过扩展Cucumber来生成高质量的文档。

首先,我们可以使用Capybara在对某个正在执行的Step进行截图。Capybara提供的截图功能可以保留当前Step的运行状态,通过图片更容易理解当时的上下文,这些图片拼在一起,其实就是一个完整的用户操作流程。

其次,我们可以通过给Step添加详细描述来解决Report不给力的问题。我们可以给每一个Specification文件创建一个相对应的描述文件,描述文件由两部分组成,一部分是Step的标题,另一部分是详细描述Step的内容。只要通过文本匹配就可以找到某个Step的详细描述,再加上之前对Step的截图,拼在一起就可以生成一个高质量的文档了。

举个例子,如果我们有这样一个Specification文件

Scenario: Customer has a tied agent policy
so last name is required

Given I have a "TiedAgent" policy
When I submit my policy number
Then I should be asked for my last name

创建一个对应的描述文件,文件类型是Markdown。

=====================
Given I have a "TiedAgent" policy
=====================
##Detail Information**
**In this step, you are assigned a "TieAgent" policy.**
![screenshot-1](./i-have-a-tied-agent-policy.png)
You can click [here](http://example.com) for more information
=====================
....

在上面的文件中,第一部分是Step标题,用来匹配Specification文件中的Step,第二部分是Markdown类型的片段,里面有图片、超链接等富文本元素,可以更好地帮助我们理解业务。

最后,通过Cucumber中提供的AfterStep Hook完成文档的生成。比如这样

AfterStep(‘@active-doc‘) do |scenario|
    @step ||= 0
    @doc ||= ActiveDocument.new
    @doc.generate(scenario, scenario.steps[@step].name)
    @step += 1
end

代码中的ActiveDocument是自己实现的,它把丰富的HTML内容和截图整合在一起,然后把Specification中所有的Step拼接在一起,就生成了一个Specification的文档。这样的文档相比之前提到的Cucumber Report具有更高的可读性,同时也具有更强的灵活性,因为文档是通过HTML展现的,我们可以添加更多的内容,比如Specification文档之间的跳转链接,或者提前录制的一段视频放入文档中,甚至可以加上第三方css和js库让文档变得更加引人入胜。

原来生活可以更美的

随着BDD的发展,越来越多的工具进入了我们的视野。我们应该认清团队的需求,结合团队的特点选择合适工具,不要盲目的随大流。下面我来列举一些具有代表性的工具,推荐给不同类型的团队。

Cucumber

简单来说,Cucumber实际上是一款有一定文档性、可以帮助团队沟通合作的、提供自动化测试功能的工具。特点是上手简单、社区活跃、文档表现力不足。所以如果团队刚开始尝试BDD,更看重自动化测试方面,而对需求文档化要求不高,Cucumber是一个不错的选择。同时Cucumber目前支持Ruby, C#, JVM, JS和C++,众多平台也是一个加分项。

Concordion

与Cucumber相比,Concordion提供了更好的文档支持。Concordion的Specification是HTML格式的,我们再也不用生搬硬套的使用Given/When/Then进行功能描述了。在HTML文件中,我们可以更加自由的描述业务需求,同时可以增加好看的样式,添加更友好的交互,放入更多的视频和图片等等。

总而言之一句话,HTML比纯文本更加灵活强大,适合阅读。同时我们也要清楚HTML的学习和维护成本相比纯文本更加昂贵,非技术的人可能很难单独完成。和技术人员结对完成,或者在技术人员完成后进行审查也是一个不错的选择。但由于Concordion目前只对C#和JAVA支持较好,所以如果团队刚好用到C#和Java,并且非常看重文档化需求,那么Concordion要比Cucumber更加适合你们。在下面的例子中,我们使用Concordion生成了“教学评估”相关的需求文档,并且使用了shower.js增强了用户交互,在保证软件功能的同时,带来了更好的阅读体验。

交互性更好地需求文档,内容组织合理,阅读体验好。

其中一个需求的详细描述,同时也是自动化测试。

Gauge

Gauge也在文档方面进行了改善,Gauge的Specification文件由Markdown组成,相对纯文本有了一定程度的提升,但还是不如Concordion灵活。Gauge使用Go编写,天然支持并发运行,相比之下性能要更加有优势。同时Gauge支持多语言实现,目前支持Java、C#和Ruby,相比Cucumber在跨平台式需要整个切换工具,Gauge更容易做跨平台。虽然目前Gauge处在开发阶段,但依然值得关注。

总结一下

  1. BDD不是工具,而是一套流程和一系列实践。它需要团队成员的通力合作,可以帮助整个团队更好的理解业务,理解软件。
  2. Cucumber作为支持BDD的一种工具,不单单是自动化测试工具。在解决了Cucumber的一些问题后,团队可以更加有效的使用。
  3. Cucumber、Concordion和Guage各有不同,选择一款适合团队自身需要的工具,也能保证团队顺利运作,少走弯路。

好了少年,我只能帮你到这里了,接下来BDD之路就看你自己的了。

点击这里浏览演讲视频。

时间: 2024-10-06 11:27:14

醒醒吧少年,只用Cucumber不能帮助你BDD的相关文章

Android 学习笔记(一) 该醒醒了

时间过的真快,转眼2015年多都快过了四分之一了,这一年.net大新闻莫过于.net 开源了,visual studio 免费了,net 真正要跨平台了, visual studio 可以做android开发.IOS开发! 身为一个.net 开发者,绝对值得庆祝,高兴过后静下心想想,似乎自己错过了太多,这些年移动开发可谓如火如荼,但似乎好像一直和自己没啥关系,去年学了6个月的window phone开发,做出一个小应用来,近一年了下载量还是保持着个位数,没办法,用户量太低了,这不,前两天的3.1

桌面开发者的界面故事,该醒醒了

本文我们只谈界面. 大部分人最开始学习编程是Console,搞个计算器啥的,后来高级一点能做一个俄罗斯方块出来.很羡慕那些能做出界面的,于是大二学了MFC,一开始看<深入浅出>怎么都搞不懂,后来我们班的一个女生教了我两个小时,我一下子通畅了,用GDI半个月苦哈哈的做了第一个当时觉得还能看得界面(不用任何控件哦)连箭头都是用三根线拼起来的! 后来学习了C#,当时是一本速成的C#开发100例,看完<实现简单的播放器>以后,“我靠,怎么这么简单!”于是便投入了WinForm的怀抱.然后做

醒醒,Android开发居然只有cv最顺手,你还会什么?

作为一个Android开发,现在的你已经开发多少年了? 你的代码质量有没有随着经验的增加而提高?没有的话就需要反思了. 现在来分享一个有六年经验Android开发,都学到了什么? 一,学习能力想要成长,学习能力尤为重要 我们一直有句老话,学如逆水行舟,不进则退.就像我们Android进阶,需要学习的高级内容比较多 1)Java语言进阶与Android相关技术内核像 泛型,多线程,反射,JVM,Java IO,注解,序列化等 2)App开发框架知识体系(app亦对象)Android 2013~20

你自认为了解微信小程序?醒醒吧!

小程序目前被炒得沸沸扬扬,无数业内业外人士都对此雄心勃勃,希望占据先机,借此一统江湖,千秋万代.这再次证明一点,微信想让什么火,什么就能火.这种能力目前在国内估计也是无人能出其右了-- 好了,废话不多话,言归正传.作为一个要成为成功人士的男人,利用国庆的时间,我好好的研究了一下微信小程序,发现网上很多言论对于微信小程序的言论,在一定区间存在理解上的误区.接下来的内容,我假定你已经初步的了解过微信小程序,如果你还不了解,请移步开发文档,然后再回来阅读本文. 一.小程序到底是不是Html5 关于这一

对于delphi for linux心存梦想的同学,彻底醒醒吧

Borland 曾经多次做市场调查,很多人呼吁推出delphi for linux.可是直到kylix发展到第三版,borland才明白:一个内置C编译器环境并与之关系紧密的免费OS,没有人愿意花钱去购买一款开发工具为它开发应用.很多时候,对于delphi for linux的需求表现,其实仅仅是windows程序员对于linux可能的市场崛起大约会带来的淘汰感,而幻想无需费力,就可以应对linux开发,其实这些人并没有任何linux开发计划.Borland感觉自己被涮了,于是坚决终止kylix

看看百度和携程面临的问题,游戏公司是不是也该醒醒了?

最近一周,可以说互联网全行业的朋友圈,都被百度贴吧.携程机票两件事刷屏了. 因为身处不同的细分行业,更不敢说自己明辨是非,所以不好妄加评论这两件事的是非曲直,但对我触动最大的,却是因为这两件事背后的共性问题,以及手游行业应该向何处去的思考. 乍一看,这两件事完全不搭.但背后,其实折射的或许是同一个问题.也就是,当移动互联网的增长红利被基本吃完后,靠粗放.甚至粗暴的流量变现模式,还能维系多久?以及,手游行业能不能找到一个挣到增量收入,吃相比较优雅,能赢得尊敬的出路? 致我们终将逝去的流量为王时代

还在玩北京的×××得朋友,还在想着回血戒赌吗?多少家破人亡的,醒醒吧,

各位老哥大家好,我 老家山东省济宁市人,在济南工作,做夜场.本是一个很有上进心,很积极的一个人,自从接触了网络×××---北京塞车.所有的一切都变了!那是2016年的9月份,我当时上班也挺好也挺积极,还有些存款.我老婆是一个很放心我的人,对我没有任何约束,包括金钱方面.她从来不要钱,把自己赚的钱,也都放到我这里....我记得有一天上班,一个很好的朋友同事,来店里送东西,我看他微信看的什么,他说是小塞车.他说一天营300块钱就不玩了,我劝他最好别玩,×××没有什么好下场,他一笑而过.我说营得再多我

《DevOps实践:驭DevOps之力强化技术栈并优化IT运行》

DevOps实践:驭DevOps之力强化技术栈并优化IT运行 主旨 这本书并非坐而论道,而是介绍了DevOps全流程中的许多实践,以及相应工具的运用.虽然随着时代的推移,工具将来可能会过时,但是这些实践的应用和相应的方法是不会过时的,所以对于其中各种实践必要性和相关方法的讲解,是特别值得注意的.作者认为一切皆代码,所以各个章节是围绕代码的生命周期展开的,提到了这些环节的实践: 管理代码 构建代码 测试代码 部署代码 监控代码 DevOps和持续交付简介 DevOps的由来 Patrick Deb

Cucumber测试驱动开发

Cucumber是一种BDD实践开发工具,属于敏捷开发的组成部分. 在敏捷开发中,对用户进行需求分析时,不是像传统的P&D的开发方式,首先编写大量的用户需求分析文档,而是通过一个个User Story来进行用户需求的分析. User Story的编写,也就是Cucumber的Gherkin编写.由一个个feature组成.User Story的编写应该符合SMART原则,即简明详细.可测量.可以在一个迭代内实现.有商业价值,并且可测试. BDD开发流程 首先应该在编写具体的代码前,先编写测试文件