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

前端之家收集整理的这篇文章主要介绍了行为驱动开发(BDD)你准备好了吗?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

最近在研究行为驱动开发(Behavior Drive Development ),那什么是行为驱动开发呢?
根据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标准,其有下面的关键字,很好理解,
• Feature
• Background
• Scenario
• Given
• When
• Then
• And
• But
• *
• Scenario Outline
• Examples
这种特性文件,客户,项目经理,BA,QA都能看懂,因为其就是一个故事点或者需求点,而且通过特定的工具,比如cucumber的工具,能够把其自动转换成为代码。开发人员根据生产的代码,断言一些预期的行为,并根据这些语言,完成相应的代码实现,在代码实现的基础上,进行重构; 这样就为
一个项目中的各个人员了解项目的需求,实现提供了一个很好的交互桥梁。下面是其一个交互的过程。

如果是传统的方式,其交互方式,应该是,

通过对比,大家是不是发现BDD的这种方式,把用户或者客户真正的通过Feature文件联系在一起了,其沟通是顺畅的,QA,BA,开发,测试,客户,用户可以通过这一媒介,进行高效无障碍的沟通,而不是像传统的方式,通过BA进行二次转达,从而丢失了很多重要的需求。
由此可见,其BDD的好处如下:

  • 减少浪费
  • 节省成本
  • 容易并且安全的适应变化
  • 因为少了中间的转达环节,从而能够快速交付产品

下面看一个简单的例子,

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

通过上面的Example的表格是不是更加容易的理解当前case的意图了。
当Feature和Example文件都完成后,借助于第三方的开源框架实现,比如Cucumber,jBehave,SpecFlow等把Feature和Example装换成代码,然后通过第层次的单元测试框架,
比如JUnit,NUnit,Spock,RSpec,结合测试驱动开发,从而把业务代码的逻辑实现。真是一举多得。
笔者就以Cucumber和JUnit为例,举一个BDD的例子吧。
大家对JUnit比较熟悉,但是对Cucumber可能会相对陌生一点,笔者就花一点笔墨,简单介绍了一下Cucumber。Cucumer是一个实现了BDD的一个框架,其支持下面的语言和框架集成,

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

1. Maven库的依赖

  1. <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">
  2. <modelVersion>4.0.0</modelVersion>
  3.  
  4. <groupId>uk.co.claysnow</groupId>
  5. <version>1.0</version>
  6. <packaging>jar</packaging>
  7. <name>Cucumber-JVM Book ATM Example</name>
  8. <artifactId>atm-example</artifactId>
  9.  
  10. <properties>
  11. <cucumber.version>1.2.0</cucumber.version>
  12. <junit.version>4.11</junit.version>
  13. <picocontainer.version>2.14.2</picocontainer.version>
  14. </properties>
  15.  
  16. <dependencies>
  17. <dependency>
  18. <groupId>info.cukes</groupId>
  19. <artifactId>cucumber-junit</artifactId>
  20. <version>${cucumber.version}</version>
  21. <scope>test</scope>
  22. </dependency>
  23. <dependency>
  24. <groupId>info.cukes</groupId>
  25. <artifactId>cucumber-picocontainer</artifactId>
  26. <version>${cucumber.version}</version>
  27. <scope>test</scope>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.picocontainer</groupId>
  31. <artifactId>picocontainer</artifactId>
  32. <version>${picocontainer.version}</version>
  33. <scope>test</scope>
  34. </dependency>
  35. </dependencies>
  36.  
  37. <build>
  38. <plugins>
  39. <plugin>
  40. <groupId>org.apache.maven.plugins</groupId>
  41. <artifactId>maven-surefire-plugin</artifactId>
  42. <version>2.12.2</version>
  43. <configuration>
  44. <argLine>-Duser.language=en</argLine>
  45. <argLine>-Xmx1024m</argLine>
  46. <argLine>-XX:MaxPermSize=256m</argLine>
  47. <argLine>-Dfile.encoding=UTF-8</argLine>
  48. <useFile>false</useFile>
  49. </configuration>
  50. </plugin>
  51.  
  52. </plugins>
  53. </build>
  54.  
  55. </project>

2. 安装Cucumber Eclipse插件

为了支持Feature的Gherkin语法,我们需要在Eclipse安装下面的插件
https://cucumber.io/cucumber-eclipse/update-site
具体安装方法,请到百度或者google搜索。下面看一个具体的例子:

3. Feature文件

  1. Feature: Cash Withdrawal
  2. Scenario: Successful withdrawal from an account in credit
  3. Given I have deposited @H_421_404@$100.00 in my account
  4. When I withdraw @H_421_404@$20
  5. Then @H_421_404@$20 should be dispensed

4. 生成的Java Specification文件

  1. import cucumber.api.java.en.*;
  2. import cucumber.api.Transform;
  3.  
  4. import org.junit.*;
  5.  
  6. import support.KnowsTheDomain;
  7. import transforms.MoneyConverter;
  8.  
  9. public class AccountSteps {
  10. KnowsTheDomain helper;
  11.  
  12. public AccountSteps(KnowsTheDomain helper) {
  13. this.helper = helper;
  14. }
  15.  
  16. @Given("^I have deposited (\\$\\d+\\.\\d+) in my account$")
  17. public void iHaveDeposited$InMyAccount(@Transform(MoneyConverter.class) Money amount)
  18. throws Throwable {
  19. helper.getMyAccount().deposit(amount);
  20.  
  21. Assert.assertEquals("Incorrect account balance -",amount,helper.getMyAccount().getBalance());
  22. }
  23. }
  1. import cucumber.api.java.en.*;
  2.  
  3. import org.junit.*;
  4.  
  5. import support.KnowsTheDomain;
  6.  
  7. import transforms.MoneyConverter;
  8.  
  9. public class CashSlotSteps {
  10.  
  11. KnowsTheDomain helper;
  12.  
  13. public CashSlotSteps(KnowsTheDomain helper) {
  14. this.helper = helper;
  15. }
  16.  
  17. @Given("^\\$(\\d+) should be dispensed$")
  18. public void $ShouldBeDispensed(int dollars) throws Throwable {
  19. Assert.assertEquals("Incorrect amount dispensed -",dollars,helper.getCashSlot().getContents());
  20. }
  21. }
  1. import cucumber.api.java.en.*;
  2.  
  3. import support.KnowsTheDomain;
  4.  
  5. public class TellerSteps {
  6.  
  7. KnowsTheDomain helper;
  8.  
  9. public TellerSteps(KnowsTheDomain helper) {
  10. this.helper = helper;
  11. }
  12.  
  13. @When("^I withdraw \\$(\\d+)$")
  14. public void iWithdraw$(int amount) throws Throwable {
  15. helper.getTeller().withdrawFrom(helper.getMyAccount(),amount);
  16. }
  17. }

5. Cucumber 测试的运行入口

  1. import cucumber.api.junit.Cucumber;
  2. import cucumber.api.CucumberOptions;
  3. import cucumber.api.SnippetType;
  4. import org.junit.runner.RunWith;
  5.  
  6. @RunWith(Cucumber.class)
  7. @CucumberOptions(plugin="pretty",snippets=SnippetType.CAMELCASE)
  8. public class RunCukesTest {
  9. }

6. 具体的实现

  1. public class Account {
  2. private Money balance = new Money();
  3.  
  4. public void deposit(Money amount) {
  5. balance = balance.add(amount);
  6. }
  7.  
  8. public Money getBalance() {
  9. return balance;
  10. }
  11. }
  12. public class CashSlot {
  13. private int contents;
  14.  
  15. public int getContents() {
  16. return contents;
  17. }
  18.  
  19. public void dispense(int dollars){
  20. contents = dollars;
  21. }
  22. }
  23. import java.util.regex.Matcher;
  24. import java.util.regex.Pattern;
  25.  
  26. public final class Money {
  27. private final int dollars;
  28. private final int cents;
  29.  
  30. public Money() {
  31. this.dollars = 0;
  32. this.cents = 0;
  33. }
  34.  
  35. public Money(int dollars,int cents) {
  36. this.dollars = dollars;
  37. this.cents = cents;
  38. }
  39.  
  40. public Money(String amount) {
  41. Pattern pattern = Pattern.compile("^[^\\d]*([\\d]+)\\.([\\d][\\d])$");
  42. Matcher matcher = pattern.matcher(amount);
  43.  
  44. matcher.find();
  45. this.dollars = Integer.parseInt(matcher.group(1));
  46. this.cents = Integer.parseInt(matcher.group(2));
  47. }
  48.  
  49. public int dollars() {
  50. return dollars;
  51. }
  52.  
  53. public int cents() {
  54. return cents;
  55. }
  56.  
  57. public Money add(Money amount){
  58. int newCents = cents + amount.cents();
  59. int newDollars = dollars + amount.dollars();
  60.  
  61. if (newCents >= 100){
  62. newCents -= 100;
  63. newDollars++;
  64. }
  65.  
  66. return new Money(newDollars,newCents);
  67. }
  68.  
  69. public Money minus(Money amount){
  70. int newCents = cents - amount.cents();
  71. int newDollars = dollars - amount.dollars();
  72.  
  73. if (newCents < 0){
  74. newCents += 100;
  75. newDollars--;
  76. }
  77.  
  78. return new Money(newDollars,newCents);
  79. }
  80.  
  81. @Override
  82. public boolean equals(Object other){
  83. boolean equal = false;
  84.  
  85. if (other instanceof Money){
  86. Money otherMoney = (Money)other;
  87. equal = (this.dollars() == otherMoney.dollars()
  88. && this.cents() == otherMoney.cents());
  89. }
  90.  
  91. return equal;
  92. }
  93.  
  94. @Override
  95. public String toString() {
  96. return String.format("$%01d.%02d",this.dollars(),this.cents());
  97. }
  98. }
  99. public class Teller {
  100. private CashSlot cashSlot;
  101.  
  102. public Teller(CashSlot cashSlot) {
  103. this.cashSlot = cashSlot;
  104. }
  105.  
  106. public void withdrawFrom(Account account,int dollars) {
  107. cashSlot.dispense(dollars);
  108. }
  109. }

从上面的例子可以看出,其实现的本质和原理如下:

夜深了,该睡了,行为驱动开发的介绍过一段落,行为驱动开发(BDD)你准备好了吗?

猜你在找的设计模式相关文章