行为驱动开发(BDD)研究

网友投稿 838 2022-05-29

1      引言

在软件工程中,行为驱动开发(BDD)是一种敏捷软件开发过程,它鼓励软件项目中的开发人员、QA和非技术或业务参与者之间的协作,它鼓励团队使用对话和具体的例子来正式形成对应用程序应该如何运行的共识理解。它源于测试驱动开发(TDD)。 行为驱动开发将TDD的一般技术和原则与领域驱动设计和面向对象分析与设计的思想相结合,为软件开发和管理团队提供共享的工具和过程,以协同合作来开发软件。

2      行为驱动开发(BDD)

尽管BDD主要是一种关于软件开发应该如何管理业务利益和技术洞察力的思想,但BDD的实践确实假设了使用专门的软件工具来支持开发过程,尽管这些工具通常是专门为BDD项目开发的,但也可以看作是支持测试驱动开发的工具化的专门形式。这些工具的作用是为BDD的核心主题(共识语言)的形成增加了自动化。

行为驱动开发(BDD)研究

BDD在很大程度上是通过使用简单的领域专用语言(DSL),并附以自然语言结构(例如,类似于英语的句子)来表达行为和预期结果,从而使得BDD的工程通俗易懂。长期以来,测试脚本一直是DSL的流行应用,其复杂程度不一。BDD被认为是一种有效的技术实践,特别是当要解决的业务问题的 "问题空间 "比较复杂时,BDD的优势就会非常明显。

2.1    历史

行为驱动开发是测试驱动开发的延伸:利用简单的、特定领域的脚本语言进行开发。这些 DSL 将结构化的自然语言语句转换为可执行的测试。 其结果是更接近于给定功能的验收标准和用于验证该功能的测试。因此,它是一般TDD测试的自然延伸。

BDD的重点是:

从哪里开始

哪些需要测试,哪些不需要测试

一次测试需要包含多少内容

如何调用测试

如何分析测试失败的原因

BDD的核心是重新思考单元测试和验收测试的方法,以避免可能产生的问题。例如,BDD建议单元测试的名称应该是以条件动词开头的整句(例如英语中的 "应该"),并且应该按照业务价值的顺序来写。验收测试应该使用用户案例故事的标准敏捷框架来写。"作为一个[角色],我希望有[这样的功能]来获得[什么好处]"。验收标准应该用场景来写,并以类的形式实现。 给定[初始场境],当[事件发生]时,然后[确保一些结果]。

基于此,很多人经过多年的发展,最终将BDD框架定义为软件项目中的开发人员、QA和非技术人员或业务参与者的沟通和协作框架,在2009年11月在伦敦举行的 "敏捷规范、BDD和测试eXchange "会议上,Dan North对BDD作了如下描述:

BDD是新一代、从外到内、基于拉取、多利益相关的、多规模、高自动化的敏捷方法。它是一个具有明确定义了产出的交互循环,从而实现了正常工作中的、经过测试的软件产品的交付。

在2013年GOTO大会上接受Dan North采访时,Liz Keogh将BDD定义为:

它用测试案例故事来探讨应用程序的运行方式,并就这些案例故事展开深入讨论。

Dan North 创建了一个 BDD 框架 JBehave,随后又为 Ruby 创建了一个基于案例故事的 BDD 框架 RBehave,该框架后来被集成到 RSpec 项目中。 他还与 David Chelimsky、Aslak Hellesøy 等人合作开发了 RSpec,还编写了《The RSpec Book: Behaviour Driven Development with RSpec, Cucumber, and Friends》。RSpec中第一个基于案例故事的框架,后来被主要由Aslak Hellesøy开发的Cucumber所取代。作为Cucumber测试框架的一部分,Capybara就是一款基于Web的测试的自动化软件。

2.2    BDD的原则

测试驱动开发是一种软件开发方法,其本质上说,对于每一个软件单元,软件开发人员必须:

该功能单元定义一个测试集。

让测试失败。

然后完成该功能单元的代码实现。

最后验证该功能单元的代码实现是否使测试集成功。

上面这个定义是不具体的,因为它允许从高级软件需求、低级技术细节或介于两者之间抽取任何东西来进行测试。 因此,有一种观点认为,BDD是TDD的继续发展,它比TDD做出了更具体的选择。

行为驱动开发规定,任何软件单元的测试都应该以单元的期望行为来指定。借鉴敏捷软件开发,这里的 "期望行为 "是由业务设定的需求组成的,也就是说,无论委托什么实体来构建软件单元,都要有业务价值的期望行为。

2.2.1    行为规范

在上述基本原则之后,BDD的第二个选择是关于如何指定所需的行为。在这个领域,BDD选择使用一种半正式的行为规范格式,这种格式是从面向对象分析和设计领域的用户案例故事规范中借鉴过来的。这种格式的场景方面可以看作是将豪尔逻辑应用到软件单元的行为规范中,使用场景的领域语言进行行为规范处理。

BDD规定,业务分析人员和开发人员应该在这一领域进行合作,并且应该以用户案例故事的形式来指定行为,每个用户案例故事都被明确地写在一个专门的文档中。"每个用户案例故事在某种程度上应该遵循以下结构:

l  一个明确的标题。

l  简短的介绍部分,其结构如下:

n  As a (作为):将从该功能中受益的人或角色。

n  I Want(我想要):该功能。

n  So that(以便):该功能的好处或价值。

n  对每一个具体情节的描述,结构如下:

n  Given(给定):情景开头的初始环境,用一个或多个分句表示。

n  when(当):触发情节的事件,用一个或多个分句说明。

n  then(然后):预期的结果,用一个或多个分句说明。

BDD没有任何正式的要求,但它坚持要求每个使用BDD的团队都要有一个简单的、标准化的格式来写下用户案例故事,其中包括上面列出的元素。然而,在2007年,Dan North提出了一个文本格式的模板,在不同的BDD软件工具中得到了广泛的应用。

标题: 退换货入库

作为一个店主。

我想在退货或换货的时候,将物品放回库存。

这样我就可以跟踪库存了。

情景一:退货退款的商品应该加到库存中。

鉴于之前有客户在我这里买了一件黑色的毛衣

我有三件黑色的毛衣库存。

当他们把黑色的毛衣退回来要求退款。

那么我的库存中应该有四件黑色毛衣。

情景二:退换货的商品应该更新库存。

考虑到之前有客户从我这里买了一件蓝色的衣服

而我有两件蓝色的衣服在库存中

和三件黑色服装的库存。

当他们把蓝色的衣服换成黑色的衣服时。

那我应该有三件蓝色的衣服在库存中

和两件黑色服装的库存。

上述场景是以声明性而非强制性的方式来表达的,在业务语言中,没有提及交互发生的UI元素。

这种格式被称为Gherkin语言,其语法类似于上面的例子。然而,Gherkin这个词是Cucumber、JBehave、Lettuce、behave和Behat等软件工具所特有的。

2.2.2    规范作为一种共识语言

行为驱动开发从领域驱动设计中借用了泛在语言的概念,泛在语言是一种(半)正式的语言,它是软件开发团队的所有成员--包括软件开发人员和非技术人员--共享的语言,该语言是所有团队成员共同使用和开发的,是讨论相关软件领域的共同手段,这样,BDD就成为软件项目中所有不同角色之间沟通的载体。

软件开发中常见的风险包括开发人员和业务利益相关者之间的沟通断裂,BDD将期望的行为规范作为项目团队成员的一种共识语言。这也是BDD坚持使用半形式化的行为规范语言的原因:某种形式化是成为一种共识语言的要求。此外,拥有这样一种共识语言,可以创建一个规范的领域模型,这样规范就可以在形式上进行推理。这个模型也是支持BDD的不同软件工具的基础。

上面给出的例子为一个正在开发的软件系统建立了一个用户案例故事。这个用户案例故事确定了一个利益相关者、一个业务效果和一个业务价值。它还描述了几个场景,每个场景都有一个前提条件、触发器和预期结果。这些部分中的每一个都是由语言中比较正式的部分准确地标识出来的(例如,给定(Given)这个词可能被认为是一个关键词),因此,可以通过理解通用语言中的正式部分的工具以某种方式进行处理。

大多数 BDD 应用程序使用基于文本的 DSL 和规范方法。然而,集成场景的图形化建模在实践中也得到了成功的应用,例如,为了测试的目的而设计出的图形化建模。

2.3    专业化的工具支持

与测试驱动的设计实践类似,行为驱动的开发假定在项目中使用专门的支持工具。就像BDD在许多方面是TDD的一个更具体的版本一样,BDD的工具与TDD的工具类似,但对开发人员提出了比基本的TDD工具更多的要求。

2.3.1    工具原则

原则上,支持BDD的工具是一个软件的测试框架,与支持TDD的工具很像。 然而,TDD工具在允许指定测试的内容方面往往是相当自由的,而BDD工具则与前面讨论过的共识语言的定义有关。

正如前面所讨论的那样,共识语言允许业务分析人员以一种也会被开发人员理解的方式写下行为需求。BDD 支持工具化的原则是让这些相同的需求文档作为测试的集合直接执行。如果因为与实现规范执行的技术工具有关的原因而无法实现,那么我们需要的是:要么改变行为需求的编写风格,要么就必须改变工具。每个工具的行为需求的具体实现方式不尽相同,但敏捷实践得出了以下一般流程:

n  通过工具读取一个规范文档。

n  该工具可以直接理解共识语言中完全正式的部分(如上面例子中的给定(Given)关键字)。在此基础上,工具将每个场景分解成有意义的子句。

n  场景中的每一个单独的子句都被转化为用户案例故事测试的某种参数。这一部分需要软件开发人员针对项目的具体工作来进行。

n  然后,框架会根据每个场景的参数来执行每个场景的测试。

Dan North开发了许多支持BDD的框架(包括JBehave和RBehave),其操作是基于他建议的记录用户案例故事的模板,这些工具使用文本描述的用例,其他几个工具(如CBehave)也纷纷效仿。然而,这种格式并不是必须的,所以其他工具也使用了其他格式。例如,Fitnesse(Fitnesse是围绕决策表构建的)也推出了BDD的支持。

2.3.2    工具化实例

当前,有几个不同平台和编程语言的BDD软件工具在项目中使用的例子可供参考。

最著名的可能是JBehave,它由Dan North、Elizabeth Keogh和其他几个人开发的。以下是该项目的一个例子:

考虑一个生命游戏的实现。一个领域专家(或业务分析员)可能想指定当有人在设置游戏网格的起始配置时应该发生什么。要做到这一点,他可能想举例说明一个人在切换单元格时所采取的一些步骤。跳过叙述部分,他可能会把下面的场景写成一个纯文本文档(这是JBehave读取的输入文档的类型):

Given给定一个5乘5的游戏 When当我在(3,2)切换单元格时 Then那么网格应该是这样的 ..... ..... ..... ..X.. ..... When当我在(3, 1)处切换单元格时 Then那么网格应该是这样的 ..... ..... ..... ..X.. ..X.. When当我在(3, 2)处切换单元格时 Then那么网格应该是这样的 ..... ..... ..... ..... ..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将通过使用注释来识别哪个方法与哪个子句一起使用,并在运行场景时按顺序调用每个方法。场景中每个子句中的文本应该与代码中给出的模板文本相匹配(例如,场景中的Given后面应该有一个形式为 " X乘以Y的游戏 "的子句)。JBehave支持子句与模板的匹配,并且内置支持从模板中选取子句并将其作为参数传递给测试代码中的方法。测试代码为每个子句类型提供了场景中的每个子句类型的实现,它与被测代码进行交互,并根据场景执行测试。在本例中:

n  TheGameIsRunning方法通过设置初始游戏网格对Given子句做出反应。

n  iToggleTheCellAt方法通过触发该子句中描述的切换事件对When子句做出反应。

n  TheGridShouldLookLike方法通过比较游戏网格的状态和场景中的预期状态来对Then子句做出反应。

这段代码的主要功能是在有案例故事的文本文件和被测代码之间架起一个桥梁。 需要注意的是,测试代码可以访问被测代码(比如上面的游戏),这点很容易理解。测试代码必须要简单,否则,开发者最终不得不为自己的测试代码再编写测试代码。

最后,为了运行测试,JBehave需要一些管道代码来识别包含场景的文本文件,并将依赖关系(如上例中的游戏)注入到测试代码中。这个管道代码在这里不做说明,因为它是JBehave的技术要求,与BDD式测试的原理没有直接关系。

2.4    案例故事与需求的对比

行为驱动开发的一个单独的子类别是使用需求作为输入语言,而不是用户案例故事。 这种风格的一个例子是RSpec工具,它也是由Dan North最初开发的。需求工具不使用用户案例故事作为测试场景的输入格式,而是使用功能需求来测试被测单元。这些需求通常比用户案例故事更具有技术性,而且通常比用户案例故事更不方便与业务人员沟通。 来看一个堆栈需求的例子:

需求: Stack   When 一个新的Stack被创建 Then 它是空的   When 一个新元素被添加到Stack Then 这个新元素会在Stack的顶部   When 一个Stack有 N 个元素时  And 元素 E 在 Stack 顶部 Then 一个弹出操作会返回 E And 新的Stack的元素个数会变为 N-1

这样的需求虽然明确指定了被测组件的行为,但对业务用户来说意义不大。因此,在BDD实践中,基于需求的测试被看作是对基于案例故事的测试的一种补充,并在较低的层次上使用。需求测试通常被看作是对自由式单元测试的替代。

像RSpec和JDave这样的需求测试工具在性质上与JBehave等工具有些不同。因为它们被视为基本的单元测试工具(如JUnit)的替代品,所以这些工具倾向于放弃案例故事和测试代码的分离,而倾向于将需求直接嵌入测试代码中。例如,一个hashtable的RSpec测试是这样的:

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

这个例子显示了如何把可读语言的需求嵌入到可执行代码中。在这种情况下,工具的选择是通过添加名为该需求的方法,将需求语言转化化为测试代码的语言。此外,这里也要注意需求的前提条件概念,也就是这个需求能够满足的前提条件。

上面测试的结果差不多是这样的:

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

2.5    三驾马车

三驾马车,是三个角色,是指产品负责人与不同的利益相关者(如QA和开发团队), 他们会以实例需求的形式讨论需求的会议。这个讨论的关键目标是通过对话并找出任何缺失的需求。这个讨论也为QA、开发团队和产品负责人提供了一个平台,让QA、开发团队和产品负责人可以互相交流和听取对方的意见,以丰富需求,并确定他们要做的产品方向是否正确。

2.5.1    业务

业务用户的作用只是定义问题(而不是提出任何解决方案)。

2.5.2    开发

开发团队的作用是提出解决问题的方法。

2.5.3    测试

测试团队的角色是质疑解决方案,通过What-If场景,提出尽可能多的不同的可能性,进行头脑风暴式的思考,并帮助解决方案更精确地处理问题。

3      参考

https://www.agilealliance.org/glossary/bdd/

https://medium.com/javascript-scene/behavior-driven-development-bdd-and-functional-testing-62084ad7f1f2

https://blog.testlodge.com/what-is-bdd/

https://cucumber.io/docs/bdd/

https://inviqa.com/blog/bdd-guide

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

软件开发

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:XML DOM 简介
下一篇:Spring Boot 参考文档阅读摘录/Spring WebFlux(三)
相关文章