这篇文章建立在对BDD有初步认识后产生的一些理解和思考。完全不知道BDD的同学建议去google一下先。
BDD的定义:
Behavior-Driven Development is about implementing an application by describing its behavior from the perspective of its stakeholders.
这里需要注意两点:
stakeholders:BDD defines a stakeholder as anyone who cares about the work we are undertaking, whether they are the people whose problem we are trying to solve—known as the core stakeholders—or the people who are going to help solve it—who we call the incidental stakeholders.
From a BDD perspective, there is no such thing as a nonfunctional requirement, just a feature with an incidental stakeholder. Even the people in the delivery team are stakeholders.
behavior:即从stakeholders的角度,看到的程序的行为。
BDD的作用:
其实BDD和agile是紧密相关的。在agile中,我们使用BDD测试作为acceptance test。也就是说,在一个sprint中,每一个程序员分配到一个任务后,将有一个BDD case与之对应,作为程序员是否完成该task的验收标准。
从这一点来说,BDD就是TDD的发展:
在一个sprint开始时,就有一个BDD case存在,开发人员将把这个case作为目标,在sprint中努力让该case通过。这就是典型的TDD,只不过同时结合了BDD一些思想,即在一开始,需要实现的test case到底是什么。
事实上,BDD出现的背景,就是开发人员在TDD时,总是不清楚到底该将一个什么样的test case作为自己TDD开发需要实现的目标。
而BDD简单的回答了这个问题:让stakeholders用一个test case告诉你,你的具体目标是什么。同时强调了,每一个开发目标都应该是系统的behavior的体现。
这里有三条BDD的原则,可以读读来加强理解:
We sum this up using the following three principles of BDD:
Enough is enough
Up-front planning, analysis, and design all have a diminishing return. We shouldn’t do less than we need to get started, but any more than that is wasted effort. This also applies to process automation. Have an automated build and deployment, but avoid trying to automate everything.
这一点显然秉承了agile的一贯原则:不做多余的事儿。
Deliver stakeholder value
If you are doing something that isn’t either delivering value or increasing your ability to deliver value, stop doing it, and do something else instead.
It’s all behavior
Whether at the code level, the application level, or beyond, we can use the same thinking and the same linguistic constructs to describe behavior at any level of granularity.
上面的两点则是BDD的核心思想。
BDD的开发过程:
下面来看看加入BDD后,开发的过程。
- 为BDD case规定统一的语法
BDD的精髓之一就是使用一种所有stakeholders都可以理解的统一语言,同时也一定是一个很nature的语言,来书写test case。
但是,这并不意味着可以随意的在BDD case中想写什么写什么。BDD case中的语法必须经过所有stakeholders讨论并达成一致。
讨论时,必须让业务人员和技术人员坐在一起。因为业务人员认为合理的BDD case的写法,技术人员在实现时可能会很困难。而技术人员提出的实现,也可能不能满足业务人员的需要。
因此,我们需要严格规定BDD 的语法,并形成一个统一的正式的文档,供所有stakeholders参考。
它看起来应该是下面的样子:
Scenario: <Component>,<Asset>,<Function>,<Test Type>
Given load complete
When there is a input file which contains <value>|<valuelist>
Then check <output> “does not contain”|”contains” <propvalue>|<propvaluelist>
这里规定了在每一个step中,可以出现的内容。其中可变的部分为symbols。例如given step只能是“load complete”,是不可变的。when step则只能是出现了一个input file,该input file中包含了一些指定的值。
同时需要对step中出现的<Component>等symbol做出详细的解释:
<Component>=CL3|PORTFOLIO
<Test Type>=Smoke|Regression|<Bug> <Test Type>
<function>=Benchmarks|RICRules|Denormalistion etc <Function>
<asset>=Equity|Option|Fixedincome|Organisation etc <Asset>
<value> = <be>.<field>:<value>
<valuelist> =” <be>|<field>|<value>”|\n<Propvaluelist>
etc
一旦达成一致,BDD case就必须严格使用规定的语法,开发人员也只会在BDD框架中支持规定的语法。除非有需求表明需要扩展已经使用的语法。
- 一个BDD case应包含的内容
在了解了BDD的基本知识后,我们知道一个BDD case称为一个story,它的的组成部分是:
A title
A narrative
acceptance criteria:实际就是一个一个的scenario。
因此第一个基本问题就是:如何定义一个story
首先feature和story的区别
Many people use the words feature and story interchangeably, but there is a subtle difference. A feature is something that delivers cohesive value to a stakeholder.A story is a piece of demonstrable functionality that shouldn’t take more thanafew days to implement. So, the feature is more useful fromthe point of viewof the stakeholder, and the story is more useful fromthe point of viewof the team delivering the feature.
也就是说,提需求的人提出一个feature,需要将feature分成一个或多个story。而每个story需要的effect应大致相同。
那么每个scenario又该如何书写呢?
每一个scenrio由step组成,通常是:
Given
When
Then
For the setup, the givens, it doesn’t matter how we get the world into a known state. 因此Given的内容应该是,要运行这个scenario,要求系统或程序处于什么状态。这里已经强调了,Given并不关心如何达到这个状态。
What matters is that the event steps have no idea how this happened and interact with the application in exactly the same way the stakeholder would. 因此Event是一个事件。
Similarly, it doesn’t matter how you verify the outcomes, just that you do.
我们举这样一个例子来更好的理解scenrio:一个拷贝文件的程序,当input folder中出现文件时,将文件上传到FTP server,同时在log中记录一行。
因此这里我们需要两个scenrio:
Given application is running --如前面所说,Given并不关心如何启动application,仅是表明当前的状态。在背后实现的时候,这句可以意味着启动程序,也可以仅仅是验证程序正在运行。
When put a file to input folder
Then find this file on FTP server --不关心如何验证
Given application is running
When put a file to input folder
Then find a record in log file
第二个基本问题:谁是写BDD case的主体?
BDD case毫无疑问是用一种通用的,易于所有stakeholders理解的,自然的,自定义的语言写成的。但这并不意味着,BDD case应该由业务人员或客户完成。
也就是说,一个BDD case, 即story涉及的内容,应该由stakeholders一起讨论完成。例如,客户提出,并和所有人商议确定在一个story中应完成的功能。
而具体的scenario,则由测试人员完成。由于采用事先定义的自然语言,所有stakeholders都可以理解,监督story完成的内容。
scenario应具备两个特征:即能被所有人理解,又能容易的被自动化运行。
所以这里隐含的意思是:虽然采用了所有人都可以理解的自然语言,但是书写test case仍然可以被理解为一个“技术活”。它仍然需要有测试经验,有技术背景的人员来完成。
否则极有可能出现业务人员写出一些看上去很合理,很自然,但实际却难以实现,或概念模糊,或测试内容不清,或覆盖逻辑不全面的test case。
因此可以说:acceptance criteria are “owned” by the tester 而story “owned” by stakeholders.
第三个基本问题:写BDD case的时机:
因为BDD是测试驱动开发TDD的更高级的阶段,所以BDD case必定要先于代码完成。但并非是在确定需求时就要完成。
it does need to happen before we do anything that requires an understanding of “done,” such as scheduling work during iteration/sprint planning.
- 开发人员将scenario自动化
开发人员需要使用JBehave或Specfolw这样的工具,将在BDD case中使用的统一语言翻译成实际的test case,并实现自动运行。
这步更多的是一些技术方面的问题,就不多说了。
- 开发人员开始编写代码,使BDD test case通过
如前面所说,在做一个迭代计划时,就应该写好BDD case。当一个迭代开始时,运行这个case,很显然会失败。开发人员需要做的就是编写代码,使case成功。通常这个过程称为:red/green/refactor
一旦case通过,就意味着代码开发完成。而代码则end up with just enough software to make the scenario work。
最后是关于BDD case和UT的思考
一个BDD case可能实际上就是一个UT,即单元测试:例如当一个scenario测试的是,当一个目录下出现txt文件时,归档。如果archive功能仅由一个class完成,那毫无疑问这就是一个UT。
如果我们已经有UT了,那就是重复测试。更何况BDD框架都是基于JUnit,NUnit这种UT测试框架做的,那么无论是使用技术和测试内容上都完全一致了。
在我看来,BDD的测试范围是很难规定的:有时scenario就是一个UT,而更多时候是集成测试。因为scenrio描述的是程序的behavior,一个behavior通常会涉及多个类,甚至多个组件。
而且tester,即scenario的编写人员,并不知道背后涉及的代码有多少。
因此一个可行的办法是,开发人员在编写代码时,如果发现scenario涉及的就是一个UT的话,就不再另外编写class对应的UT了。