行為驅動開發(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

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97