用Cucumber理解BDD行为驱动开发
在学习Cucumber时,认识了BDD(Behaviour-Driven Development)行为驱动开发这个概念。在搜索过程中,发现提及相关概念的博客或者技术网站比较少。虽然在Cucumber的官网就有介绍相关知识的文档,但是没有提供中文,而且有些专业术语也比较拗口。在记录笔记时候,就把官网这篇文档翻译成了中文,也就是这篇博客。
但是博主的知识有限,也没有来得及好好修习有关敏捷软件的内容,所以翻译不当之处,恳请读者能多多指导。
原网页(https://docs.cucumber.io/bdd/)
以下是译文:
BDD概述(BDD Overview)
-
因为误解需求或者需求模糊导致的返工
-
不愿重构代码而积累技术债(Technical debt)
-
(组织的)谷仓效应和交接问题(silos and hand-overs)导致缓慢的反馈周期
BDD目标是减小团队成员之间的交流障碍,培养(团队)更好地理解客户以及促进(团队)和现实世界的实例(example,这个单词下面都翻译成“实例”)进行持续交流。
实例(example)描述了软件怎样按照预想的运行(behave),通常说明一个特定业务规则或者需求
一个简单例子
Liz should be asked to guess again when she guesses "joke"
这个例子来自于一个猜单词的游戏,它说明了一个规则,规定一次猜测必须是5个字母。
(译者注:这个例子的解释应为“它说明了一个规则,规定当她猜测‘joke’这个单词的时候要求她再猜一次”,原来的解释可能有误)
BDD可以分成两个部分:刻意发现(Deliberate Discovery)和测试驱动开发(Test-Driven Development)
刻意发现
软件项目出错有很多原因。一个非常常见的原因是,不同组织或者不同团队的人对于软件如何运行和软件正要尝试解决什么问题,意见不一。
Ignorance is the single greatest impediment to throughput
无知是产能最大的阻碍。 --Dan North
在开发开始之前,想方设法去发现(在问题域上)未知(的知识)的团队会更有生产效率。因为那样就会带来更少的返工。那样做的最有效的方法是通过在以下主要利益相关者之间进行对话和合作:
-
产品负责人
-
业务分析师
-
领域专家
-
用户
-
程序员
-
UX设计师
-
测试人员
-
运维工程师
-
可能还有其他一些人员
Cucumber是通过一种叫做实例映射(Example Mapping)的一种为了一起提出实例的简单技术(a simple technique for coming up with examples together),来完成(上面说的发现未知)这件事。
当(在软件生命周期中担任)不同角色的人们谈论一个具体实例的时候,他们通常会在问题域上发现很多(知识)。
他们产生的实例在一起就会变成自动化测试和描述系统如何运行的活文件(living documentation)。
测试驱动开发
测试驱动开发(Test-Driven Development,TDD)是一种自动化测试在编码之前编写的开发技术。开发者使用那些测试来驱动(drive)开发。
TDD可以在从验收测试到单元测试(范围内)的不同粒度上使用。 TDD的BDD风格(The BDD flavour of TDD)使用自然语言来描述测试。它们可以被非程序员理解,(同时)也能基于它使用实例映射来协同地创建实例。
Gherkin是一种关于自然语言测试的简单语法,Cucumber是可以执行这些测试的工具。
编码后测试不是BDD。
许多人在编码后编写测试,甚至在用Cucumber时也如此。但这既不是BDD,也不是TDD。因为测试并没有在编写完成后(用于)驱动(代码)实现。
TDD/BDD并不属于测试
一个关于TDD和BDD的常见误解认为,它们是测试技术。但实际并不如此。就像其名字所说的那样,TDD和BDD属于软件开发。
它是(让你)接近你的设计(目标)和迫使你在编码前思考你想要的结果以及API的过程。
自动化测试是TDD和BDD的副产品。
Example Mapping
在你把用户故事(user story)投入到开发中之前,非常关键的一点是要通过对话来阐明并遵循验收标准(acceptance criteria)。
实例映射是一个使得这个对话简短和富于生产效率的简单方法。
它是怎么工作的
具体实例是一个帮助我们探索和理解问题域的好方法。它们(指具体实例)为我们的验收测试打下一个好基础。
当谈论实例(example)时,其它也值得获取的事情可能会出现在对话中:
-
概述一个实例集合或表述其它限制的规则
-
对话或制造的假设中不能被回答的问题
-
已经发现或剖析过的,同时因延期而逾期的新的用户故事
我们可以(通过)索引卡片(的方式)获取这些不同种类的信息,(通过)导图(放入方式)可以组织它们(的关系):
-
我们在黄色卡片上写下故事,然后把它放在顶部
-
每一个验收标准或规则写在蓝色卡片上,然后放在黄色故事卡片下面
-
说明这些规则的实例写在绿色卡片上,然后根据相关规则置放
-
会话中不能回答的问题就写在红色卡片上,这样我们的会话可以(暂时不用关注这些问题)继续进行
我们可以保持(对话)进行下去,直到项目组对故事的边界足够清晰感到满意,或者我们用完了时间。
更多信息
译者注:原网页有一些链接,请直接访问原网页
实例(Examples)
好的BDD实例是具体的而不是抽象的。它们提及(mention)人物和地点的名字、确切的时间和总数,还有与软件的问题域有关的一切事物。
好的实例不会提及技术细节。
Imagine it's 1922
大多数软件做的都是人们手工就能完成但不是那么有效率的事情。
努力尝试提出不对技术做任何假设的实例来。想像现在是1922年,一个没有计算机的年代。
谁做了什么(Who Does What?)
谁应该写Gherkin文档,谁应该写步骤定义(step definition)呢?
产品负责人,业务分析师,程序员和测试人员经常搞不清楚谁应该承担什么责任。
答案取决于几个因素,比如团队结构、技能、文化、做事方法(process)等。
三剑客(The Three Amigos)
三剑客是一个将用户故事带到并转变为整洁、彻头彻尾的Gherkin场景(Scenario)的会议。它至少包含三种(角色的,或者剑客的)声音:
-
产品负责人----更关心应用的边界(scope)。这涉及到将用户故事转译成一系列的特征(Features)。当测试人员提出边界情况(edge case)时,产品负责人负有决定哪一种(情况)在(应用)边界内的责任。
-
测试人员----产生很多的场景和边界情况。应用将会怎样崩溃?在这些特征里面,什么用户故事还没有被我们考虑进去呢?
-
研发人员----为场景添加步骤(Steps),思考深入每个需求的细节。应用怎样执行?有哪些不为人知的障碍和需求(需要越过和满足)?
这些对话会带来很好的测试,因为每一个人从不同的角度来看产品。由此,让这些(担任)角色(的人员)参与讨论,一起发现实例是十分必要的。实例映射和事件风暴(Event Storming)对于发现实例来说,都是很好的协同分析技术。
最后,限制这些会议(指上面这个三剑客会议)参与人员只能为这三种人,或者在项目开始阶段只举行一次这样的会议,都是不合理的。(应该)持续地提炼你的特征(features)和与任何人合作来深刻理解如何讨论、开发和测试你的应用。
书写Gherkin(Writing Gherkin)
任何特征的第一份手稿最好是由或者与“领域专家”合作书写。这种人典型(情况下)是一个非程序员并且总是那些站在用户或者业务角度、了解特征领域(features's domain)的人。
然后程序员会重做(go over)场景,提炼步骤来(使得情景容易)阐明和提升易测性(testability)。然后结果被领域专家评审,确保(项目)意图没有被程序员改变。
这个循环会重复下去直到参与(项目)的每个人都对场景(是否)精确地描述了哪些东西以一种可测的方式被需要(what is wanted in a testable manner)感到满意。
书写特征(Writing Features)
Cucumber测试从特征的角度来书写。每一个特征(Feature)包含一个或多个场景(Scenario)。
让我们从一个特征文件(Feature file)开始:
Feature: Explaining Cucumber
In order to gain an understanding of the Cucumber testing system
As a non-programmer
I want to have an overview of Cucumber that is understandable by non-geeks
Scenario: A worker seeks an overview of Cucumber
Given I have a coworker who knows a lot about Cucumber
When I ask my coworker to give an overview of how Cucumber works
And I listen to their explanation
Then I should have a basic understanding of Cucumber
注意场景并没有深入软件(在这个例子下是coworker)细节。它一直关注于特征用来服务的人(这种例子下是a non-programmer)的立场。
每一个特征文件可以有一个位于顶部的特征描述和任意数量的场景。
Feature
那一行是特征的名字,名字应该是一个简短的标签。
In order to
表现拥有特征的原因/正当理由,通常来说,应该要与项目的核心目标或者“业务价值”匹配:
-
项目收入
-
增益收入
-
管理开销
-
提升品牌价值
-
让产品变得卓越
-
为你的客户提供更多价值
As a
描述由特征服务的人物/用户的角色。
I want
用一句话解释特征应该做什么。
所以,这三行涵盖了为什么(Why)、谁(Who)和什么(What)。接下来,这份文档(指的是上面这个特征文件)将会用场景(Scenario)来深入介绍"How"。
场景(Scenarios)
一个特征可以有任意数量的场景。
当然,如果你在一个特征中拥有太多的场景,事实上你可能在描述不止一个特征。这种情况发生时,我们建议你把文档(特征文件)分成几个独立的特征定义(Feature definition)。这里所说的“太多”是主观的,应由你决定何时该分解一个特征。
第一行简单描述场景用来包含的内容。如果你不能用一句话来描述你的场景(不能用连写句run-on sentence),那么很可能你尝试包含的内容太多,这个时候应该把特征分成多个场景。
在那之后跟着的是一些步骤(Step)的组合----以关键字Given
,When
和Then
(典型情况就是这个次序)开头的几行。
你可以有多个使用相同关键字的行。比如在Given there is something
后跟着Given I have another thing。为了增加可读性,你可以使用关键字
And和
But来替换。比如在Given there is something
之后跟着And I have another thing
。
一般说来,任何Given
步骤所在行应该只描述一件事。如果你在一个步骤中有像和
这样的词,你可能在描述的内容超过了一个步骤,你应该把它分成多个步骤。
比如说:
When I fill in the "Name" field and the "Adress" field
变成:
When I fill in the "Name" field
And I fill in the "Adress" field
Cucumber特征最好需要满足一致性。不能用不同的方式来说同一件事,而是每次都用同一种方式。
比如:
Given I am logged in
和
Given I have logged in to the site
有完全一样的意思,所以最好选择一句并在之后的每个需要登录的场景中使用同一句。
书写更好的Gherkin(Writing better Gherkin)
有一些使得你的Gherkin更好的方式。
’描述行为(Describe behaviour)
你的场景(Scenario)应该描述系统预期的行为,并不是实现。换句话说,应该描述 什么What
而不是 怎样How
。
比如,对于一个验证身份的场景,你应该写:
When "Bob" logs in
而不是
Given I visit "/login"
When I enter "Bob" in the "user name" field
And I enter "tester" in the "password" field
And I press the "login" button
Then I should see the "welcome" page
在第一个例子中,When "Bob" logs in
是一个功能要求(functional requirement)。第二个长很多的例子是一个过程参照(procedural reference)。功能要求是特征,但是过程属于实现细节。
那种方式下,当一个特征的实现改变了,你只需要改变过程步骤。行为不需要改变,因为只是实现发生了改变。事实上,当你写一个特征项时,应该问你自己一个问题:如果实现改变了,(特征的)措辞也需要改变吗?
如果答案是yes