跳转到主要内容
Chinese, Simplified

在软件工程中,行为驱动开发(BDD)是一种敏捷软件开发过程它鼓励开发人员,QA和软件项目中的非技术或业务参与者之间的协作。[1] [2] [3]它鼓励团队使用对话和具体的例子来形式化对应用程序应该如何表现的共同理解。[4]它来自于测试驱动开发(TDD)。[1] [2] [5] [6] [模糊] [7]行为驱动开发将TDD的一般技术和原理与域驱动设计对象的思想相结合面向分析和设计,为软件开发和管理团队提供共享工具和共享流程,以协作进行软件开发。[2] [7]

虽然BDD主要是关于如何通过商业利益和技术洞察来管理软件开发的想法,但BDD的做法确实假设使用专门的软件工具来支持开发过程。[5]虽然这些工具通常专门用于BDD项目,但它们可以被视为支持测试驱动开发的工具的专用形式。这些工具用于为无处不在的语言添加自动化,这是BDD的核心主题。

通过使用可以表达行为和预期结果的自然语言构造(例如,类似英语的句子)的简单领域特定语言(DSL),BDD在很大程度上得到了促进。测试脚本长期以来一直是DSL的流行应用,具有不同程度的复杂性。 BDD被认为是一种有效的技术实践,尤其是当要解决的业务问题的“问题空间”很复杂时。

历史


行为驱动的开发是测试驱动开发的扩展:[9]开发利用了一种简单的,特定于域的脚本语言。这些DSL将结构化自然语言语句转换为可执行测试。结果是与给定函数的验收标准和用于验证该功能的测试的关系更密切。因此,它通常是TDD测试的自然延伸。

BDD专注于:

  1. 从哪里开始
  2. 测试什么和不测试什么
  3. 一次性测试多少钱
  4. 怎么称呼测试
  5. 如何理解测试失败的原因

从本质上讲,BDD正在重新思考单元测试和验收测试的方法,以避免自然产生的问题。例如,BDD建议单元测试名称是以条件动词开头的整个句子(例如英语中的“should”),并且应按商业价值的顺序编写。验收测试应该使用用户故事的标准敏捷框架编写:“作为[角色]我想要[特征]以便[好处]”。接受标准应根据情景编写并作为类实现:给定[初始上下文],[事件发生],然后[确保一些结果]。

从这一点开始,许多人在几年内开发了BDD框架,最终将其构建为开发人员,QA以及软件项目中的非技术或业务参与者的通信和协作框架。[10]在2009年11月伦敦的“敏捷规范,BDD和测试交换”期间,Dan North [11]对BDD进行了以下描述:

BDD是第二代,从外到内,基于拉动,多利益相关方,多规模,高自动化,敏捷的方法。它描述了与明确定义的输出相互作用的循环,从而提供了重要的可运行的,经过测试的软件。

在2013年GOTO会议上与Dan North的访谈中,Liz Keogh [12]将BDD定义为:

它使用示例来讨论应用程序的行为方式......以及有关这些示例的对话。 [13]

Dan North创建了一个BDD框架,JBehave,然后是Ruby的故事级BDD框架,名为RBehave [14],后来被整合到RSpec项目中。[15]他还与David Chelimsky,AslakHellesøy和其他人合作开发了RSpec,并撰写了“RSpec书籍:RSpec,Cucumber和Friends的行为驱动开发”。 RSpec的第一个基于故事的框架后来被主要由AslakHellesøy开发的Cucumber取代。 Capybara是Cucumber测试框架的一部分,是一种基于Web的测试自动化软件。

BDD的原则


测试驱动开发是一种软件开发方法,它基本上表明对于每个软件单元,软件开发人员必须:

  1. 首先为单位定义一个测试集;
  2. 使测试失败;
  3. 然后实施该单位;
  4. 最后验证单元的实现是否使测试成功。

该定义非常具体,因为它允许根据高级软件需求,低级技术细节或其间的任何内容进行测试。因此,看待BDD的一种方式是TDD的持续发展,它比TDD做出更具体的选择。

行为驱动的开发规定,任何软件单元的测试都应根据单元的期望行为来规定。[5] [7] [1]借助敏捷软件开发,在这种情况下,“期望的行为”包括业务设定的要求 - 即,对正在建设的软件单元委托的任何实体具有商业价值的期望行为。[5] [1]在BDD实践中,这被称为BDD是一种“从外到内”的活动。[16]

行为规范


根据这一基本选择,BDD做出的第二个选择涉及如何指定所需的行为。在这一领域,BDD选择使用半正式格式进行行为规范,这种格式借鉴了面向对象分析和设计领域的用户故事规范。这种格式的场景方面可以被视为Hoare逻辑应用于使用情境的域语言的软件单元的行为规范。

BDD指定业务分析师和开发人员应该在这个领域进行协作,并且应该根据用户故事指定行为,每个用户故事都明确记录在专用文档中。[1] [16]每个用户故事都应该以某种方式遵循以下结构:[5] [16]

标题:故事应该有清晰明确的标题。
叙述
一个简短的介绍部分,详细说明

  • 谁(who):(哪个企业或项目角色)是故事的驱动者或主要利益相关者(从故事中获得商业利益的演员)
  • 什么(how):影响利益相关者想要的故事
  • 原因(why):利益相关者的商业价值将来自这种效应

验收标准或方案
对叙述的每个具体情况的描述。这种情况具有以下结构:

  • 它首先指定在场景开始时假定为true的初始条件。这可能包含一个或多个子句。
  • 然后它会说明哪个事件触发了场景的开始。
  • 最后,它在一个或多个条款中陈述了预期的结果。

BDD对于如何写下这些用户故事没有任何正式要求,但它确实坚持使用BDD的每个团队都提出了一种简单,标准化的格式来记录用户故事,其中包括上面列出的元素。[5 ] [16]然而,2007年Dan North提出了一个文本格式的模板,该模板在不同的BDD软件工具中得到了广泛的应用。[16]这种格式的一个非常简短的例子可能如下所示:


故事:返回库存

作为店主
为了跟踪库存
我想在退货时将商品添加回库存。

场景1:退款项目应退回库存
鉴于客户以前从我那里买过一件黑色毛衣
并且 我有三件黑色毛衣。
他们退回黑色毛衣退款
然后我应该有四件黑色毛衣。

场景2:替换项目应返回库存
鉴于客户以前从我那里买过蓝色服装
并且我有两件蓝色服装库存
并且还有三件黑色服装现货。
他们将蓝色衣服换回黑色替换件时
然后我应该有三件蓝色服装库存
并且还有两件黑色服装现货。

理想情况下,这些场景是以声明方式而非命令性方式表达的 - 在商业语言中,没有引用交互发生的UI元素。[17]

此格式称为Gherkin语言,其语法类似于上面的示例。然而,Gherkin这个术语特定于Cucumber,JBehave,behave和Behat软件工具。[18] [19] [20] [21]

规范作为普遍存在的语言


行为驱动的开发借用了域驱动设计中普遍存在的语言的概念。[5] [7]无处不在的语言是一种(半)正式语言,由软件开发团队的所有成员共享 - 软件开发人员和非技术人员。[22]所讨论的语言都是由所有团队成员使用和开发的,作为讨论相关软件领域的常用手段。[22]通过这种方式,BDD成为软件项目中所有不同角色之间进行通信的工具。[5] [23]

软件开发的一个共同风险包括开发人员和业务利益相关者之间的沟通中断。[24] BDD使用所需行为的规范作为项目团队成员的普遍使用的语言。这就是BDD坚持使用半正式语言进行行为规范的原因:一些形式是成为普遍存在的语言的必要条件。[5]此外,拥有这样一种无处不在的语言会创建一个规范的域模型,因此可以正式推理出规范。[25]此模型也是可用的不同BDD支持软件工具的基础。

上面给出的示例为正在开发的软件系统建立用户故事。此用户故事标识了利益相关者,业务效果和业务价值。它还描述了几种情景,每种情景都有前提条件,触发条件和预期结果。这些部分中的每一部分都由语言的更正式部分确定(例如,术语Given可能被视为关键字),因此可以通过理解普遍存在的语言的正式部分的工具以某种方式处理。

大多数BDD应用程序使用基于文本的DSL和规范方法。然而,集成场景的图形建模也已成功应用于实践中,例如用于测试目的。 [26]

 

专业的工具支持


与测试驱动的设计实践非常相似,行为驱动的开发假定在项目中使用专门的支持工具。尽管BDD在很多方面都是更具体的TDD版本,但BDD的工具类似于TDD的工具,但对开发人员的要求比基本的TDD工具要多。[需要澄清] [引证需要]

工具原理


原则上,BDD支持工具是软件的测试框架,非常类似于支持TDD的工具。但是,在允许指定测试的TDD工具往往是完全自由格式的情况下,BDD工具与前面讨论的普遍存在的语言的定义相关联。

正如所讨论的,无处不在的语言允许业务分析师以开发人员也能理解的方式写下行为需求。 BDD支持工具的原理是使这些相同的需求文档可以直接作为一组测试来执行。如果由于与能够执行规范的技术工具相关的原因而无法实现这一点,那么必须改变编写行为要求的风格或者必须改变工具。[27]行为要求的确切实现因工具而异,但敏捷实践提出了以下一般过程:

  1. 工具读取规范文档。
  2. 工具直接理解普遍存在的语言的完整形式部分(例如上面例子中的Given关键字)。基于此,该工具将每个场景分解为有意义的子句。
  3. 场景中的每个单独子句都转换为某种参数,用于测试用户素材。这部分需要软件开发人员针对特定项目的工作。
  4. 然后,框架使用该场景中的参数为每个场景执行测试。

Dan North开发了许多支持BDD的框架(包括JBehave和RBehave),其操作基于他建议用于记录用户故事的模板。[5]这些工具使用了用例的文本描述,其他几个工具(如CBehave)也遵循了这些描述。但是,此格式不是必需的,因此还有其他工具也使用其他格式。例如,Fitnesse(围绕决策表构建)也被用于推出BDD。[28]

 

工具示例


对于不同的平台和编程语言,当今的项目中使用了几种不同的BDD软件工具示例。

可能最着名的是JBehave,它由Dan North,Elizabeth Keogh和其他几位人员开发。[29]以下是该项目的一个例子:[19]

考虑一下生命游戏的实现。域专家(或业务分析师)可能希望指定当某人设置游戏网格的起始配置时应该发生什么。要做到这一点,他可能想举例说明切换单元格的人采取的一些步骤。跳过叙述部分,他可以通过将以下场景写入纯文本文档(这是JBehave读取的输入文档的类型)来完成此操作:

Given a 5 by 5 game

When I toggle the cell at (3, 2)

Then the grid should look like

.....

.....

.....

..X..

.....

When I toggle the cell at (3, 1)

Then the grid should look like

.....

.....

.....

..X..

..X..

When I toggle the cell at (3, 2)

Then the grid should look like

.....

.....

.....

.....

..X..


粗体字不是输入的一部分;它包括在这里,以显示哪些单词被识别为正式语言。 JBehave识别术语Given(作为定义场景开始的前提条件),When(作为事件触发器)和Then(作为后置条件,必须验证为触发后的动作的结果)。基于此,JBehave能够读取包含场景的文本文件并将其解析为子句(设置子句,然后是具有可验证条件的三个事件触发器)。然后JBehave接受这些子句并将它们传递给能够设置测试,响应事件触发器和验证结果的代码。此代码必须由项目团队中的开发人员编写(在Java中,因为这是JBehave所基于的平台)。在这种情况下,代码可能如下所示:

private Game game;
private StringRenderer renderer;

@Given("a $width by $height game")
public void theGameIsRunning(int width, int height) {
    game = new Game(width, height);
    renderer = new StringRenderer();
    game.setObserver(renderer);
}
    
@When("I toggle the cell at ($column, $row)")
public void iToggleTheCellAt(int column, int row) {
    game.toggleCellAt(column, row);
}

@Then("the grid should look like $grid")
public void theGridShouldLookLike(String grid) {
    assertThat(renderer.asString(), equalTo(grid));
}


代码具有针对场景中每种类型的子句的方法。 JBehave将通过使用注释来识别哪个方法与哪个子句一起使用,并在运行场景时按顺序调用每个方法。场景中每个子句中的文本应该与该子句的代码中给出的模板文本相匹配(例如,场景中的给定后面应该是“X by Y游戏”形式的子句) 。 JBehave支持将子句与模板匹配,并内置支持从模板中挑选术语并将它们作为参数传递给测试代码中的方法。测试代码为场景中的每个子句类型提供实现,该场景与正在测试的代码交互并基于场景执行测试。在这种情况下:

  1. theGameIsRunning方法通过设置初始游戏网格对Given子句做出反应。
  2. iToggleTheCellAt方法通过触发子句中描述的toggle事件来响应When子句。
  3. theGridShouldLookLike方法通过将游戏网格的状态与场景中的预期状态进行比较来对Then子句做出反应。

此代码的主要功能是成为具有故事的文本文件和正在测试的代码之间的桥梁。请注意,测试代码可以访问正在测试的代码(在本例中是Game的实例),并且本质上非常简单。测试代码必须简单,否则开发人员最终不得不为他的测试编写测试。

最后,为了运行测试,JBehave需要一些管道代码来识别包含场景的文本文件,并将依赖项(如Game的实例)注入测试代码。此管道代码未在此处说明,因为它是JBehave的技术要求,并不直接与BDD样式测试的原理相关。

 

故事与规范


行为驱动开发的单独子类由使用规范作为输入语言而非用户故事的工具形成。这种风格的一个例子是RSpec工具,它最初也是由Dan North开发的。规范工具不将用户故事用作测试场景的输入格式,而是使用正在测试的单元的功能规范。这些规范通常比用户故事具有更多的技术性质,与用户故事相比,与业务人员的沟通通常不太方便。[5] [30]堆栈规范的示例可能如下所示:

Specification: Stack

When a new stack is created
Then it is empty

When an element is added to the stack
Then that element is at the top of the stack

When a stack has N elements 
And element E is on top of the stack
Then a pop operation returns E
And the new size of the stack is N-1

这样的规范可以准确地指定被测试组件的行为,但对业务用户来说意义不大。因此,基于规范的测试在BDD实践中被视为基于故事的测试的补充,并且在较低级别上运行。规范测试通常被视为自由格式单元测试的替代品。[30]

RSpec和JDave等规范测试工具在性质上与JBehave等工具略有不同。由于它们被视为JUnit等基本单元测试工具的替代品,因此这些工具倾向于放弃故事和测试代码的分离,而更喜欢将规范直接嵌入到测试代码中。例如,散列表的RSpec测试可能如下所示:[31]

describe Hash do
  let(:hash) { Hash[:hello, 'world'] }

  it { expect(Hash.new).to eq({}) }

  it "hashes the correct information in a key" do
    expect(hash[:hello]).to eq('world')
  end

  it 'includes key' do
    hash.keys.include?(:hello).should be true
  end
end

此示例显示嵌入可执行代码中的可读语言的规范。在这种情况下,工具的选择是通过添加名为it和should的方法将规范语言形式化为测试代码的语言。还有一个规范前提条件的概念 - 前一节确定了规范所基于的前提条件。

测试结果将是:

 Hash
   should eq {}
   includes key
   hashes the correct information in a key

 

三个朋友


Three Amigos,也称为“规范研讨会”,是产品负责人以QA和开发团队等不同利益相关者的形式通过实例讨论要求的会议。这次讨论的关键目标是触发对话并认真识别任何缺失。讨论还为QA,开发团队和产品所有者提供了一个平台,汇集和听取彼此的观点,以丰富需求,并确保我们是否正在构建正确的产品。软件工程中的三个Amigos一词是由George Dinwiddie创造的[32]

The three Amigos are

  • Business
  • Development
  • Testing

示例映射


示例映射由Matt Wynne介绍。[33]他创造了一种技术,可以在30分钟内引导对话分解任何产品积压物品。主要目标是从故事中获取规则,并为每个规则提取不同的情景。[34]

SHE QC


SHE QC修饰[35]是另一种将复杂的用户故事整理成场景的技巧。整理结构经过优化,可以在团队成员之间进行正确且集中的对话,包括业务,运营,开发和QA。这个过程涉及头脑风暴的双钻石规则[36],本次会议的结果是一套未回答的问题和故事场景。这种做法的结构和时间安排,有助于团队每天不断改进故事。[37]

See also[edit]

原文:https://en.wikipedia.org/wiki/Behavior-driven_development

本文:

讨论:请加入知识星球或者小红圈【首席架构师圈】

Article
知识星球
 
微信公众号
 
视频号