专栏名称: GitChat技术杂谈
GitChat是新时代的学习工具。
目录
相关文章推荐
程序猿  ·  离谱!下载 DeepSeek 将判 20 ... ·  昨天  
程序员小灰  ·  DeepSeek俱乐部,6000人了! ·  2 天前  
程序员的那些事  ·  《人民日报》批谷歌安卓系统:假开源、真垄断 ·  3 天前  
程序猿  ·  本地部署 DeepSeek ... ·  4 天前  
51好读  ›  专栏  ›  GitChat技术杂谈

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

GitChat技术杂谈  · 公众号  · 程序员  · 2017-10-16 07:15

正文

本文来自作者 冰尘 GitChat 上分享「 行为驱动开发(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)加入进来,


  4.0.0
  com.icedust.bdd
  bddKata
  0.0.1-SNAPSHOT
  bddKata
  bddKata
  
        1.2.0
        4.11
        2.14.2
    

    
        
            info.cukes
            cucumber-junit
            ${cucumber.version}
            test
        
        
            info.cukes
            cucumber-picocontainer
            ${cucumber.version}
            test
        
        
            org.picocontainer
            picocontainer
            ${picocontainer.version}
            test
        
    

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  "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 并输出结果







请到「今天看啥」查看全文