| 编辑推荐: | 
                                   
                                   
                                    本文来自于csdn,本文主要通过行为驱动开发BDD的例子说明为什么使用行为驱动开发,用什么框架来做行为驱动开发,行为驱动开发的核心原理,希望对您的学习有所帮助。  | 
                                   
                                  | 
                             
                           
                            
                          
                            根据BDD in action一书的介绍,  
                            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提供了一种通用的,简单的,结构化的描述语言,这种语言既可以是英语也可以是其他本地的语言,通过他能够很方便让项目成员和业务干系人非常顺畅的沟通需求,及时这些干系人不懂的任何编程语言。下面就是一个例子。 
                            
                          是不是很好读懂,上面其实就叫Feature(特性文件),其遵循的Gherkin标准,其有下面的关键字,很好理解, 
                             
                           1.Feature  
                           2.Background  
                           3.Scenario  
                           4.Given  
                           5.When  
                           6.Then  
                           7.And  
                           8.But  
                           9.*  
                           10.Scenario Outline  
                           11.Examples  
                           这种特性文件,客户,项目经理,BA,QA都能看懂,因为其就是一个故事点或者需求点,而且通过特定的工具,比如cucumber的工具,能够把其自动转换成为代码。开发人员根据自动生成的代码,断言一些预期的行为,并根据这些行为,完成相应的代码实现,在代码实现的基础上,进行重构; 
                            这样就为一个项目中的各个人员了解项目的需求,实现提供了一个很好的交互桥梁。下面是其一个交互的过程。 
                              
                          如果是传统的方式,其交互方式,应该是, 
                             
                          通过对比,大家是不是发现BDD的这种方式,把用户或者客户真正的通过Feature文件联系在一起了,其沟通是顺畅的,QA,BA,开发,测试,客户,用户可以通过这一媒介,进行高效无障碍的沟通,而不是像传统的方式,通过BA进行二次转达,从而丢失了很多重要的需求。 
                             
                            由此可见,其BDD的好处如下: 
                            减少浪费 
                            节省成本 
                            容易并且安全的适应变化 
                            因为少了中间的转达环节,从而能够快速交付产品 
                           下面看一个简单的例子 
                             
                          从上图可以看出,当一个需求过来的时候,先通过项目干系人都能理解的Feature文件,描述项目的User 
                            Story, 有的里面还有详细生动的数据范例(example),从而能够让所有的人更加容易理解其需求, 
                            比如 
                             
                          通过上面的数据范例(example)的表格是不是更加容易的理解当前case的意图了。当Feature和Example文件都完成后,借助于第三方的开源框架实现,比如Cucumber,jBehave,SpecFlow等把Feature和Example装换成代码,然后通过第层次的单元测试框架,比如JUnit,NUnit,Spock,RSpec,结合测试驱动开发,从而把业务代码的逻辑实现。真是一举多得。 
                           笔者就以Cucumber和JUnit为例,举一个BDD的例子吧。大家对JUnit比较熟悉,但是对Cucumber可能会相对陌生一点,笔者就花一点笔墨,简单介绍了一下Cucumber。Cucumer是一个实现了BDD的一个框架,其支持下面的语言和框架集成, 
                            
                          是不是感觉很强大啊!!!! 下面进入实战, 
                            1. Maven库的依赖 
                          
                             
                              <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/maven-v4_0_0.xsd">  
                                <modelVersion>4.0.0</modelVersion>  
                                <groupId>uk.co.claysnow</groupId>  
                                <version>1.0</version>  <packaging>jar</packaging>  
                                <name>Cucumber-JVM Book ATM Example</name>  
                                <artifactId>atm-example</artifactId>  
                                <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>  <build>  <plugins>  
                                <plugin>  <groupId>org.apache.maven.plugins</groupId>  
                                <artifactId>maven-surefire-plugin</artifactId>  
                                <version>2.12.2</version>  <configuration>  
                                <argLine>-Duser.language=en</argLine>  
                                <argLine>-Xmx1024m</argLine>  <argLine>-XX:MaxPermSize=256m</argLine>  
                                <argLine>-Dfile.encoding=UTF-8</argLine>  
                                <useFile>false</useFile>  </configuration>  
                                </plugin>  </plugins>  </build> 
                                </project> 
                               | 
                             
                           
                          
                            2. 安装Cucumber Eclipse插件 
                            为了支持Feature的Gherkin语法,我们需要在Eclipse安装下面的插件:  
                            https://cucumber.io/cucumber-eclipse/update-site  
                            具体安装方法,请到百度或者google搜索。下面看一个具体的例子: 
                           3. Feature文件 
                          
                             
                              Feature: Cash 
                                Withdrawal 
                                Scenario: Successful withdrawal from an account 
                                in credit 
                                Given I have deposited $100.00 in my account 
                                When I withdraw $20 
                                Then $20 should be dispensed 
                               | 
                             
                           
                          4. 生成的Java Specification文件  
                          
                             
                              import cucumber.api.java.en.*; 
                                import cucumber.api.Transform; 
                                import org.junit.*; 
                                import support.KnowsTheDomain; 
                                import transforms.MoneyConverter; 
                                public class AccountSteps { 
                                KnowsTheDomain helper; 
                                public AccountSteps(KnowsTheDomain helper) { 
                                this.helper = helper; 
                                } 
                                @Given("^I have deposited (\\$\\d+\\.\\d+) 
                                in my account$") 
                                public void iHaveDeposited$InMyAccount(@Transform(MoneyConverter.class) 
                                Money amount)  
                                throws Throwable { 
                                helper.getMyAccount().deposit(amount); 
                                Assert.assertEquals("Incorrect account balance 
                                -", amount, helper.getMyAccount().getBalance()); 
                                 
                                } 
                                } 
                               | 
                             
                            
						  
                             
                              mport cucumber.api.java.en.*; 
                                import org.junit.*; 
                                import support.KnowsTheDomain; 
                                import transforms.MoneyConverter; 
                                public class CashSlotSteps { 
                                KnowsTheDomain helper; 
                                public CashSlotSteps(KnowsTheDomain helper) { 
                                this.helper = helper; 
                                } 
                                @Given("^\\$(\\d+) should be dispensed$") 
                                public void $ShouldBeDispensed(int dollars) throws 
                                Throwable { 
                                Assert.assertEquals("Incorrect amount dispensed 
                                -", dollars,  
                                helper.getCashSlot().getContents()); 
                                } 
                                } 
                               | 
                             
                            
						  
                             
                              import cucumber.api.java.en.*; 
                                import support.KnowsTheDomain; 
                                public class TellerSteps { 
                                KnowsTheDomain helper; 
                                public TellerSteps(KnowsTheDomain helper) { 
                                this.helper = helper; 
                                } 
                                @When("^I withdraw \\$(\\d+)$") 
                                public void iWithdraw$(int amount) throws Throwable 
                                { 
                                helper.getTeller().withdrawFrom(helper.getMyAccount(), 
                                amount); 
                                } 
                                } 
                               | 
                             
                           
                          5. Cucumber 测试的运行入口 
                          
                             
                              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 RunCukesTest { 
                                } 
                               | 
                             
                           
                          6. 具体的实现 
                          
                             
                              public class 
                                Account { 
                                private Money balance = new Money(); 
                                public void deposit(Money amount) { 
                                balance = balance.add(amount); 
                                } 
                                public Money getBalance() { 
                                return balance; 
                                } 
                                } 
                                public class CashSlot { 
                                private int contents; 
                                public int getContents() { 
                                return contents; 
                                } 
                                public void dispense(int dollars){ 
                                contents = dollars; 
                                } 
                                } 
                                import java.util.regex.Matcher; 
                                import java.util.regex.Pattern; 
                                public final class Money { 
                                private final int dollars; 
                                private final int cents; 
                                public Money() { 
                                this.dollars = 0; 
                                this.cents = 0; 
                                } 
                                public Money(int dollars, int cents) { 
                                this.dollars = dollars; 
                                this.cents = cents; 
                                } 
                                public Money(String amount) { 
                                Pattern pattern = Pattern.compile("^[^\\d]*([\\d]+)\\.([\\d][\\d])$"); 
                                Matcher matcher = pattern.matcher(amount); 
                                matcher.find(); 
                                this.dollars = Integer.parseInt(matcher.group(1)); 
                                this.cents = Integer.parseInt(matcher.group(2)); 
                                } 
                                public int dollars() { 
                                return dollars; 
                                } 
                                public int cents() { 
                                return cents; 
                                } 
                                public Money add(Money amount){ 
                                int newCents = cents + amount.cents(); 
                                int newDollars = dollars + amount.dollars(); 
                                if (newCents >= 100){ 
                                newCents -= 100; 
                                newDollars++; 
                                } 
                                return new Money(newDollars, newCents); 
                                } 
                                public Money minus(Money amount){ 
                                int newCents = cents - amount.cents(); 
                                int newDollars = dollars - amount.dollars(); 
                                if (newCents < 0){ 
                                newCents += 100; 
                                newDollars--; 
                                } 
                                return new Money(newDollars, newCents); 
                                } 
                                @Override 
                                public boolean equals(Object other){ 
                                boolean equal = false; 
                                if (other instanceof Money){ 
                                Money otherMoney = (Money)other; 
                                equal = (this.dollars() == otherMoney.dollars() 
                                  && this.cents() == otherMoney.cents()); 
                                } 
                                return equal; 
                                } 
                                @Override  
                                public String toString() { 
                                return String.format("$%01d.%02d", this.dollars(), 
                                this.cents()); 
                                }  
                                } 
                                public class Teller { 
                                private CashSlot cashSlot; 
                                public Teller(CashSlot cashSlot) { 
                                this.cashSlot = cashSlot; 
                                } 
                                public void withdrawFrom(Account account, int 
                                dollars) { 
                                cashSlot.dispense(dollars); 
                                } 
                                } 
                                   | 
                             
                           
                           从上面的例子可以看出,其实现的本质和原理如下:  
                            
                          夜深了,该睡了,行为驱动开发的介绍过一段落,行为驱动开发(BDD)你准备好了吗? 
                          
                           
                            
                           |