行为驱动开发(BDD)你准备好了吗?

发表于:2017-10-16来源:GitChat作者:冰尘点击数: 标签:BDD
BDD,「Behavior Driven Development」 的缩写,中文意思,行为驱动开发,BDD本质上是一种敏捷软件开发实践,它鼓励软件项目中的开发者、测试,用户,业务分析人员等之间相互协作。

 

这个 Chat 笔者将会和大家一起探讨下面的主题:

  1. 什么是行为驱动开发(BDD)?

  2. 为什么使用行为驱动开发(BDD)?

  3. 如何做行为驱动开发(BDD)?

  4. 遗留系统适合使用行为驱动开发(BDD)吗?

  5. 总结

一、什么是行为驱动开发(BDD)?

BDD,「Behavior Driven Development」 的缩写,中文意思,行为驱动开发,BDD本质上是一种敏捷软件开发实践,它鼓励软件项目中的开发者、测试,用户,业务分析人员等之间相互协作。

BDD 最初是由 Dan North 在2003年命名,它包括验收测试和客户测试驱动等极限编程实践,是作为对测试驱动开发(TDD,Test drive development)的回应。在过去数年里,它得到了很大的发展。

为了加深大家对BDD概念的理解,咱们来看看《BDD in action》一书对BDD的定义和概括,

Behavior-Driven Development (BDD) is a set of software engineering practices 
designed to help teams build and deliver more valuable, higher quality software faster. 

It draws on Agile and lean practices including, in particular, Test-Driven Development  (TDD) and Domain-Driven Design (DDD). 

But most importantly, BDD provides a common language based on simple, structured sentences expressed in English (or in the native language of the stakeholders) that facilitate communication between project team members and business stakeholders.

翻译成中文的大概意思就是,行为驱动开发是一个软件工程的系列实践,能够帮助团队快速构建和交付更多价值和质量的软件产品。

其和敏捷已经精益的开发实践,是一脉相承的,特别是测试驱动开发,已经领域驱动开发。但是最重要的是BDD提供了一种通用的。

简单的,结构化的描述语言,这种语言既可以是英语也可以是其他本地的语言,通过他能够很方便让项目成员和业务干系人非常顺畅的沟通需求,及时这些干系人不懂的任何编程语言。

下面举1个简单易懂的栗子: 计算器的例子。

假设我们需要开发一个计算器,其 里面有一个加法的运算,那应该如何描述,才能让所有参与项目的人都能看懂呢?下面就是其中的一种写法。

上面这个例子,写的就是描述一个计算器加法的一个例子,是不是非常直观易懂,上面的文件,其实叫 Feature(特性文件)。那为什么容易看懂呢? 因为其使用了 Gherkin 语法。

那么 Gherkin 是什么呢? 其实,Gherkin语法就是使用 Given,when,then等关键字词来描述一个用户故事(User Story)。

形成一份不论是客户,业务分析人员,测试,还是开发,都能读懂的文件格式。 具体定义和用法请大家参考这个链接 ( https://cucumber.io/docs/reference ) 。

需要说明的是,请大家注意左边的红色的关键字,Feature,Scenario,Given,When,And,Then; 这些关键字其实就是 Gherkin 语法定义的标准关键字,其主要的关键字如下。

  • Feature

  • Background

  • Scenario

  • Given

  • When

  • Then

  • And

  • But

  • Scenario Outline

  • Examples

上面的关键字中有一个 examples 的关键字,这个关键字非常的好,不知道大家发现没有,人们在日常交流的过程中,有的时候,为了让大家对某一件比较复杂或者难以理解的,或者容易产生歧义的事情,喜欢举个例子,Gherkin也不例外。

为了让大家在使用行为驱动开发的过程,相互协作的各个团队之间,更好的理解需求,举个例子是一个非常好的方式。下面咱们可以看一个使用 Gherkin 中的 examples 的例子,

故事上下文如下:

虽然现在已经进入了无现金交易的时代,但是我们有的时候还是需要去银行取点现金,那我们就看一个取钱的例子吧。

上面的一个基于BDD的特性文件(Feature)中,明显的用到了Examples关键字,举了3个例子(三行),比如第一行,当前账号余额为500美元,取了50美元,我们收到了50美元,账号还剩下450美元。

这个时候,可能有读者会问,如果我们公司是国内的公司,没有英语的环境,项目经理,客户,产品经理的英语都不太好,那我用英语写的特性文件(Feature),他们都能看懂吗?,是不是还要边看边查字典啊??? 哈哈,没有关系,Gherkin语法是支持国际化的,下面是他们的一个关键字对照表。

| 英文关键字         | 中文关键字 
| ——————-    |:——————- 
| feature          | “功能”                    | 
| background       | “背景”                    | 
| scenario         | “场景”, “剧本”             | 
| scenario_outline | “场景大纲”, “剧本大纲”      | 
| examples         |”例子”                     | 
| given            |”假如”, “假设”, “假定”      | 
| when             |”当”                      | 
| then             |”那么”                    | 
| and              |”而且”, “并且”, “同时”      | 
| but              | “但是”                   |

我们可以直接把中文写在 Feature 文件中,处理和解析就交给第三方的框架的吧,比如 Cucumber。什么? Cucumber,是什么鬼东西,我查查字典,原来是“黄瓜”的意思。 

其实,亲们, Cucumber 就是实现行为驱动开发的一个开源框架而已,其中支持BDD的框架,除了Cucumber,还有 Spec,Spock 等等等开源项目,在后面分享到的如何实现 BDD 的章节中,我会为大家进一步介绍Cucumber。

二、为什么使用行为驱动开发(BDD)?

如果读者已经有过软件开发工作经验的话,应该能很快看懂传统需求挖掘,分发和使用流程,一般情况下,应该是下面的样子。

那么,通过这张图,我想您一定能立马发现,所有的需求流动和维护都是单方向的,而我们知道,软件的需求其实就是软件的目标,就是我们应该交付的产品,是我们应该要做的正确的事情。

而对于用户的需求而言,有的时候其实是很复杂的,有的时候客户在提出某一想法的时候,其实压根自己也不知道最终需要一个什么产品,只是大概模糊的知道需要实现一个功能。

而且客户的想法和最终实现这个产品的开发人员做出来的东西最终肯能会不太一样,因为开发人员可能已经开始根据最初的需求文档已经把代码实现了,QA 也把测试用例写好了。

但是 QA 根据需求文档写出的测试用例和开发人员开发出来的产品的可能根本匹配不上,好多的工作就这样白白浪费了,于是团队成员抱怨了。

另外,谁有能保证业务人员把需求文档写出来后,没有歪曲和误解商务人员告诉给他的需求和想法,开发人员能通过文档把业务分析人员写的东西全部理解透吗?有的时候业务需求文档,真的不是特别的有趣,没有例子,比较抽象,有歧义。

怎么办?怎么办?怎么办?重要话说三遍,那有没有一种媒介,可以让大家及时的,基于同一个平台的交流,而且用于交流的媒介,对于需求的描述也非常的生动,会根据以后软件的行为进行分类,并提供一些生动的例子呢? 下面我们看看 BDD 会如何做。

通过对比,大家是不是发现 BDD 的这种方式,把客户,业务分析人员,开发人员,测试人员,文档工程师,通过特性文件(Feature File)真正的联系在一起了,其沟通是顺畅的,QA,BA,开发,测试,客户,用户可以通过这一媒介,进行高效无障碍的沟通,而不是像传统的方式,通过BA进行二次转达,从而丢失了很多重要的需求。

由此可见,其BDD的好处如下:

  • 减少浪费

  • 节省成本

  • 容易并且安全的适应变化

  • 因为少了中间的转达环节,从而能够快速交付产品

三、如何做行为驱动开发(BDD)?

从上图可以看出,当一个需求过来的时候,先通过项目干系人都能理解的 Feature 文件,描述项目的 User Story, 有的里面还有详细生动的数据范例(examples),从而能够让所有的人更加容易理解其需求, 比如:

不得不说,通过上面的数据范例(examples)的表格是不是更加容易的理解当前 case 的意图了。

当 Feature 和 Example 文件都完成后,借助于第三方的开源框架实现,比如Cucumber,jBehave,SpecFlow 等把 Feature 和 Example 转换成代码,然后通过低层次的单元测试框架。

比如 JUnit,NUnit,Spock,RSpec,结合测试驱动开发(TDD),从而把业务代码的逻辑实现。

下面,笔者就以 Cucumber ( https://cucumber.io/ ) 和 JUnit 为例,举一个BDD的例子吧。大家对 JUnit 比较熟悉。

但是对 Cucumber 可能会相对陌生一点,笔者就花一点笔墨,简单介绍了一下 Cucumber。

Cucumer 是一个实现了BDD的一个框架,其支持下面的语言和框架集成, 
Cucumer 简直要逆天了,基本上所有的主流语言都支持,而且还能和市面上一些流行框架相结合。

比如自动化测试框架,Selenium; Ruby 的超级牛逼的 Web 开发框架 Ruby On Rails 等等。

是不是感觉很强大啊!!!! 下面进入实战。

咱们以 Java 代码为例子, 
结合 Cucumber,JUnit 以及 Maven 给大家演示。

3.1 建立一个 Maven 项目并添加 Cucumber 依赖库

首先,我们建立一个Maven的项目,名字就叫BDDKata,为什么叫这个名字呢?

因为针对某一种特定技术或技能进行重复性的练习,从而将其熟练掌握,这在编程领域常被人称为“编码套路”(Code Kata)。

Code Kata 的概念是由 David Thomas 提出的,他是《程序员修炼之道:从小工到专家》的作者之一,大家如果感兴趣的话可以买这本书看看,非常经典的一本书。

因为当前的例子也是针对BDD的一个简单的练习,所以我也取名就做 BDDKata。

既然是基于 Java Cucumber 的类库去实现BDD,那么我们首先要把 Cucumber相关的jar通过Maven的依赖(Dependency)加入进来,

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.icedust.bdd</groupId>
  <artifactId>bddKata</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>bddKata</name>
  <description>bddKata</description>
  <properties>
        <cucumber.version>1.2.0</cucumber.version>
        <junit.version>4.11</junit.version>
        <picocontainer.version>2.14.2</picocontainer.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>${cucumber.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-picocontainer</artifactId>
            <version>${cucumber.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.picocontainer</groupId>
            <artifactId>picocontainer</artifactId>
            <version>${picocontainer.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

3.2 安装 Cucumber Eclipse 插件

为了支持Feature的Gherkin语法,我们需要在Eclipse开发环境里面安装下面的插件:

https://cucumber.io/cucumber-eclipse/update-site

具体安装方法,请到百度或者google搜索。

3.3 新建一个 Feature 文件,编一个需求

为了简单起见,我们选择一个买服装的一个场景,下面是根据和业务人员调研后,业务分析人员得到的一个服装店收银台的一个例子 ,

Feature: Checkout on Shopping
  Scenario Outline: Checkout Shirt 
    Given the price of a "Shirt" is 200RMB 
    When I checkout <count> "Shirt"
    Then the total price should be 400RMB

    Examples:
    | count | total   |     
    | 1     | 200     | 
    | 2     | 400     |

  Scenario: Two Shirt scanned separately 
    Given the price of a "Shirt" is 300RMB 
    When I checkout 1 "Shirt"
    And I checkout 1 "Shirt"
    Then the total price should be 600RMB

  Scenario: A Shirt and an Shoes
    Given the price of a "Shirt" is 200RMB
    And the price of a "Shoes" is 300RMB 
    When I checkout 1 "Shirt"
    And I checkout 1 "Shoes"
    Then the total price should be 500RMB

里面总共有3个场景(Scenario),

第1个场景:

假设一件衬衣的价格是200,买了 n件,其价格应该是多少钱?

第2个场景:

买了两件衬衣,总共是多少钱?

第3个场景:

买了一件衬衣和一双鞋是多少钱?

3.4 运行 Feature 文件,生成 Cucumber 的步骤(Steps)代码

当我们选中这个 Feature 文件(Checkout.feature)文件的时候,我们运行 Cucumber Feature 的时候,如下图。

其部分输出如下 :

4 Scenarios (4 undefined)
15 Steps (15 undefined)
0m0.000s

You can implement missing steps with the snippets below:

@Given("^the price of a \"(.*?)\" is (\\d+)RMB$")
public void the_price_of_a_is_RMB(String arg1, int arg2) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

@When("^I checkout (\\d+) \"(.*?)\"$")
public void i_checkout(int arg1, String arg2) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

@Then("^the total price should be (\\d+)RMB$")
public void the_total_price_should_be_RMB(int arg1) throws Throwable {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

上面的代码提示我们有4个场景,15个步骤没有定义。

那么明明上面笔者说了只有3个场景,那为什么提示的时候,是4个场景呢?

原来,第一个场景描述里面有一个 Examples,里面举了2个例子,在加上后面的2个所以是4个。

15个步骤,指的是 Gherkin 关键字所对应的语句(Given,When,And,Then 等等),注意带有 Examples 的第一个场景,因为有2个 example,所以要乘以2。

根据提示,我们把胶水代码从上面的控制台输出拷贝下面,并新建一个java类:CheckoutSteps ,文件名字为 CheckoutSteps.java,内容如下 :

package com.icedust.bddkata;
import cucumber.api.java.en.*;
import cucumber.api.PendingException;

public class CheckoutSteps {
    @Given("^the price of a \"(.*?)\" is (\\d+)RMB$")
    public void the_price_of_a_is_RMB(String arg1, int arg2) throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        throw new PendingException();
    }
    @When("^I checkout (\\d+) \"(.*?)\"$")
    public void i_checkout(int arg1, String arg2) throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        throw new PendingException();
    }

    @Then("^the total price should be (\\d+)RMB$")
    public void the_total_price_should_be_RMB(int arg1) throws Throwable {
        // Write code here that turns the phrase above into concrete actions
        throw new PendingException();
    }

}

3.5 在步骤代码里面加上 JUnit 的断言并根据断言驱动业务实现

根据 BDD 的开发的原则,先写 BDD 的步骤代码(Steps)和单元测试代码,然后再写实现代码,因为实现代码还没有写,所以先写的骤代码(Steps)和单元测试代码肯定运行失败。

但是没有关系,这个时候我们就可以写业务实现代码了,然后让单元测试通过,一旦单元测试通过,我们就可以对代码进行重构,上面的步骤简称RGB(红绿蓝), 具体含义大家请参考这篇文章 ( https://msdn.microsoft.com/en-us/library/aa730844%28v=vs.80%29.aspx ) 。

3.5.1 修改步骤代码并编写单元测试

在根据 Feature 文件生成的步骤代码中(Steps)中,写上业务实现的类的对象和以及其方法,通过 Cucumber 中定义的Steps(带有When,Given,then , And的关键字),获取 Feature 文件里面的数据,最后写上单元测试的断言,具体代码如下

注意: 因为业务实现代码暂时还没有实现,比如 Checkout 类,以及 Checkout 类的中 add( ) 方法,所以 Eclipse 开发环境出现编译异常,不要紧,因为这是 BDD&TDD 的一个必经的步骤。

3.5.2 编写业务实现

下面咱们通 BDD 的测试用例来驱动业务代码的开发,驱动出来的业务代码如下 :

package com.icedust.bddkata;
public class Checkout {
    private int runningTotal = 0;

    public void add(int count, int price) { 
        runningTotal += (count * price);
    }

    public int total() { 
        return runningTotal;
    }
}

3.5.3  重新运行测试

这个时候,我们发现,CheckoutSteps 类里面的异常消失了。

那么如何自动运行单元 CheckoutSteps 定义的 Step 以及其中的单元测试呢?

这个时候,我就需要加入一个启动BDD的Step和单元测试的入口类: RunBDDTest,如下所示意。

package com.icedust.bddkata;

import cucumber.api.junit.Cucumber;
import cucumber.api.CucumberOptions;
import cucumber.api.SnippetType;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(plugin="pretty", snippets=SnippetType.CAMELCASE)
public class RunBDDTest {

}

3.5.4 运行测试类:RunBDDTest 并输出结果

选中 RunBDDTest 的类并运行单元测试,则在控制台会出现下面的输出。

从最后一句话我们可以得知,4个场景,15个步骤都运行成功了。

Feature: Checkout on Shopping

  Scenario Outline: Checkout Shirt            [90m# com/icedust/bddkata/checkout.feature:3[0m
    [36mGiven [0m[36mthe price of a "Shirt" is 200RMB[0m
    [36mWhen [0m[36mI checkout <count> "Shirt"[0m
    [36mThen [0m[36mthe total price should be <total>RMB[0m

    Examples: 

  Scenario Outline: Checkout Shirt         [90m# com/icedust/bddkata/checkout.feature:10[0m
    [32mGiven [0m[32mthe price of a "[0m[32m[1mShirt[0m[32m" is [0m[32m[1m200[0m[32mRMB[0m [90m# CheckoutSteps.the_price_of_a_is_RMB(String,int)[0m
    [32mWhen [0m[32mI checkout [0m[32m[1m1[0m[32m "[0m[32m[1mShirt[0m[32m"[0m              [90m# CheckoutSteps.i_checkout(int,String)[0m
    [32mThen [0m[32mthe total price should be [0m[32m[1m200[0m[32mRMB[0m  [90m# CheckoutSteps.the_total_price_should_be_RMB(int)[0m

  Scenario Outline: Checkout Shirt         [90m# com/icedust/bddkata/checkout.feature:11[0m
    [32mGiven [0m[32mthe price of a "[0m[32m[1mShirt[0m[32m" is [0m[32m[1m200[0m[32mRMB[0m [90m# CheckoutSteps.the_price_of_a_is_RMB(String,int)[0m
    [32mWhen [0m[32mI checkout [0m[32m[1m2[0m[32m "[0m[32m[1mShirt[0m[32m"[0m              [90m# CheckoutSteps.i_checkout(int,String)[0m
    [32mThen [0m[32mthe total price should be [0m[32m[1m400[0m[32mRMB[0m  [90m# CheckoutSteps.the_total_price_should_be_RMB(int)[0m

  Scenario: Two Shirt scanned separately   [90m# com/icedust/bddkata/checkout.feature:13[0m
    [32mGiven [0m[32mthe price of a "[0m[32m[1mShirt[0m[32m" is [0m[32m[1m300[0m[32mRMB[0m [90m# CheckoutSteps.the_price_of_a_is_RMB(String,int)[0m
    [32mWhen [0m[32mI checkout [0m[32m[1m1[0m[32m "[0m[32m[1mShirt[0m[32m"[0m              [90m# CheckoutSteps.i_checkout(int,String)[0m
    [32mAnd [0m[32mI checkout [0m[32m[1m1[0m[32m "[0m[32m[1mShirt[0m[32m"[0m               [90m# CheckoutSteps.i_checkout(int,String)[0m
    [32mThen [0m[32mthe total price should be [0m[32m[1m600[0m[32mRMB[0m  [90m# CheckoutSteps.the_total_price_should_be_RMB(int)[0m

  Scenario: A Shirt and an Shoes           [90m# com/icedust/bddkata/checkout.feature:19[0m
    [32mGiven [0m[32mthe price of a "[0m[32m[1mShirt[0m[32m" is [0m[32m[1m200[0m[32mRMB[0m [90m# CheckoutSteps.the_price_of_a_is_RMB(String,int)[0m
    [32mAnd [0m[32mthe price of a "[0m[32m[1mShoes[0m[32m" is [0m[32m[1m300[0m[32mRMB[0m   [90m# CheckoutSteps.the_price_of_a_is_RMB(String,int)[0m
    [32mWhen [0m[32mI checkout [0m[32m[1m1[0m[32m "[0m[32m[1mShirt[0m[32m"[0m              [90m# CheckoutSteps.i_checkout(int,String)[0m
    [32mAnd [0m[32mI checkout [0m[32m[1m1[0m[32m "[0m[32m[1mShoes[0m[32m"[0m               [90m# CheckoutSteps.i_checkout(int,String)[0m
    [32mThen [0m[32mthe total price should be [0m[32m[1m500[0m[32mRMB[0m  [90m# CheckoutSteps.the_total_price_should_be_RMB(int)[0m

4 Scenarios ([32m4 passed[0m)
15 Steps ([32m15 passed[0m)
0m0.099s

3.5.5 重构代码

运行成功后,可以重构代码,重构的代码不仅仅只包括重构业务实现代码,还包括重构 BDD 的测试代码。

重构代码,100个人可能就有100种重构的方式,具体如何重构代码,这又是另外一个话题,读者可以看 Martin Fowler 著写的《重构 改善既有代码的设计》以及 Robert C. Martin 写的《代码整洁之道 程序员的职业素养》,笔者就不在赘述。感兴趣的读者,可以自行重构。

上面的整个流程其实可以用下面的2张图,完美描述。

上面的例子,列举的是一个新系统的中使用BDD的例子,那么对于遗留系统该如何BDD呢?

四、复杂遗留系统适合使用行为驱动开发(BDD)吗?

笔者去年有一段时间,特别针对了这个问题进行了研究,因为遗留系统,一般年限很长,用到的技术多种多样。而且有的部分代码有单元测试,有的部分的代码没有单元测试。

有的单元测试,一看就知道不是先写测试再写业务实现,而是先写业务实现,再写单元测试的,且单元测试都是补上去的。

针对这种情况,笔者认为,如果要把一个大型的,维护了10几年的遗留项目,彻底推翻,使用 BDD 和 TDD 进行重写是不现实的,而且随着复杂性的提高,其成本也是很高的。

除非有特别的理由,否则很难得到领导的同意。那这个时候,我们该怎么办?难道BDD在复杂的遗留系统里面就用不上了吗?

其实,非也,冰冻三尺,非一日之寒,下面是笔者的一些个人建议,仅供参考,不喜勿喷。

首先要给大家灌输 BDD 和 TDD 的好处。教会大家如何做 BDD 和 TDD。因为在遗留系统中有的时候,会添加一些新的特性,这个时候,可以在不动别的功能特性的基础上,尝试对新添加的功能使用 BDD 和 TDD。

根据笔者观察,因为复杂的遗留系统,很多代码很难测试,而且还有很多私有方法,静态方法,有 final 修饰的,很难写单元测试的。

这个时候,其实瓶颈就在如何 Mock 这些复杂的上下文场景。推荐大家使用PowerMock。

如果有时间的话,先从重构现有的单元测试的用例开始,只要单元测试的用例好维护,好扩展了,大家才能有动力和心情,继续写更多更好的单元测试,从而培养大家先写单元测试的感觉。

如果您有更好的建议和方案,也可以在笔者的这片文章下面留言,笔者将会把其加入到本文中来。

五、总结

感谢大家看完了本文,通过本文,笔者给大家分享了什么是 BDD,为什么要做 BDD,如何来做 BDD,最后探讨了如何做 BDD 已经在复杂的遗留系统上应该使用什么策略来做 BDD。 

其实,我们知道在软件开发或者维护过程中,基本的主流角色有,开发,测试,客户,用户,项目经理,运维人员等。

而在生产和开发一个软件的过程中,处处充满风险,这个时候,从宏观角度来说,做正确的事是最重要的;从微观角度来说,正确的做事也很重要。

其中,做正确的事是最最重要的,如果大家看过玩过传递猜词游戏的话,应该知道,第一个人看到一个正确的词语,然后用动作表达出来,然第二个人猜意思,然后再用动作表达给第三个人。。。。。 往往到了最后,意思可能大相径庭。

在软件开发的需求分析和实现阶段又何尝不是这样呢?本来客户需要的是一辆自行车,结果却可能得到一辆摩托车。

这一切的一切都是沟通惹的祸,其实就是没有做正确且被期望的事情。而BDD(行为驱动开发), 就是为了解决这个问题。

我们知道,BDD就是先写需求和功能点描述,这种描述客户,经理,开发,测试都能看懂,然后根据这些特性文件,列出一个个生动形象的场景,即使没有写过任何代码的人都能看懂,而且是基于文本的。

开发人员把这些特性文件转换成具体的测试用例并驱动业务实现,目的说白了,就是让开发出来的系统正是客户所需要的,从而保证了做正确的事情,正如上面的图所示,客户需要的是自行车,就真正开发出来自行车,而不是开发出摩托车。

参考资料

《BDD In Action》

《The Cucumber for Java Book(Pragmatic,2015)》

原文转自:http://gitbook.cn/gitchat/activity/59b0fdfea7179e475ef60b8c