±à¼ÍƼö: |
±¾ÎÄÀ´×ÔÓÚ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.
·Òë³ÉÖÐÎĵĴó¸ÅÒâ˼¾ÍÊÇ£¬ÐÐΪÇý¶¯¿ª·¢ÊÇÒ»¸öÈí¼þ¹¤³ÌµÄϵÁÐʵ¼ù£¬Äܹ»°ïÖúÍŶӿìËÙ¹¹½¨ºÍ½»¸¶¸ü¶à¼ÛÖµºÍÖÊÁ¿µÄÈí¼þ²úÆ·¡£ÆäºÍÃô½ÝÒѾ¾«ÒæµÄ¿ª·¢Êµ¼ù£¬ÊÇÒ»ÂöÏà³ÐµÄ£¬ÌرðÊDzâÊÔÇý¶¯¿ª·¢£¬ÒѾÁìÓòÇý¶¯¿ª·¢¡£µ«ÊÇ×îÖØÒªµÄÊÇBDDÌṩÁËÒ»ÖÖͨÓõ쬼òµ¥µÄ£¬½á¹¹»¯µÄÃèÊöÓïÑÔ£¬ÕâÖÖÓïÑԼȿÉÒÔÊÇÓ¢ÓïÒ²¿ÉÒÔÊÇÆäËû±¾µØµÄÓïÑÔ£¬Í¨¹ýËûÄܹ»ºÜ·½±ãÈÃÏîÄ¿³ÉÔ±ºÍÒµÎñ¸ÉϵÈ˷dz£Ë³³©µÄ¹µÍ¨ÐèÇ󣬼°Ê±ÕâЩ¸ÉϵÈ˲»¶®µÄÈκαà³ÌÓïÑÔ¡£ÏÂÃæ¾ÍÊÇÒ»¸öÀý×Ó¡£

ÊDz»ÊǺܺöÁ¶®£¬ÉÏÃæÆäʵ¾Í½Ð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µÄ¹¤¾ß£¬Äܹ»°ÑÆä×Ô¶¯×ª»»³ÉΪ´úÂë¡£¿ª·¢ÈËÔ±¸ù¾Ý×Ô¶¯Éú³ÉµÄ´úÂ룬¶ÏÑÔһЩԤÆÚµÄÐÐΪ£¬²¢¸ù¾ÝÕâЩÐÐΪ£¬Íê³ÉÏàÓ¦µÄ´úÂëʵÏÖ£¬ÔÚ´úÂëʵÏֵĻù´¡ÉÏ£¬½øÐÐÖØ¹¹£»
ÕâÑù¾ÍΪһ¸öÏîÄ¿Öеĸ÷¸öÈËÔ±Á˽âÏîÄ¿µÄÐèÇó£¬ÊµÏÖÌṩÁËÒ»¸öºÜºÃµÄ½»»¥ÇÅÁº¡£ÏÂÃæÊÇÆäÒ»¸ö½»»¥µÄ¹ý³Ì¡£

Èç¹ûÊÇ´«Í³µÄ·½Ê½£¬Æä½»»¥·½Ê½£¬Ó¦¸ÃÊÇ£¬

ͨ¹ý¶Ô±È£¬´ó¼ÒÊDz»ÊÇ·¢ÏÖBDDµÄÕâÖÖ·½Ê½£¬°ÑÓû§»òÕ߿ͻ§ÕæÕýµÄͨ¹ýFeatureÎļþÁªÏµÔÚÒ»ÆðÁË£¬Æä¹µÍ¨ÊÇ˳³©µÄ£¬QA£¬BA£¬¿ª·¢£¬²âÊÔ£¬¿Í»§£¬Óû§¿ÉÒÔͨ¹ýÕâһý½é£¬½øÐиßЧÎÞÕϰµÄ¹µÍ¨£¬¶ø²»ÊÇÏñ´«Í³µÄ·½Ê½£¬Í¨¹ýBA½øÐжþ´Îת´ï£¬´Ó¶ø¶ªÊ§Á˺ܶàÖØÒªµÄÐèÇó¡£
Óɴ˿ɼû£¬ÆäBDDµÄºÃ´¦ÈçÏ£º
¼õÉÙÀË·Ñ
½ÚÊ¡³É±¾
ÈÝÒײ¢ÇÒ°²È«µÄÊÊÓ¦±ä»¯
ÒòΪÉÙÁËÖмäµÄת´ï»·½Ú£¬´Ó¶øÄܹ»¿ìËÙ½»¸¶²úÆ·
ÏÂÃæ¿´Ò»¸ö¼òµ¥µÄÀý×Ó

´ÓÉÏͼ¿ÉÒÔ¿´³ö£¬µ±Ò»¸öÐèÇó¹ýÀ´µÄʱºò£¬ÏÈͨ¹ýÏîÄ¿¸ÉϵÈ˶¼ÄÜÀí½âµÄFeatureÎļþ£¬ÃèÊöÏîÄ¿µÄUser
Story£¬ ÓеÄÀïÃæ»¹ÓÐÏêϸÉú¶¯µÄÊý¾Ý·¶Àý£¨example£©£¬´Ó¶øÄܹ»ÈÃËùÓеÄÈ˸ü¼ÓÈÝÒ×Àí½âÆäÐèÇó£¬
±ÈÈç

ͨ¹ýÉÏÃæµÄÊý¾Ý·¶Àý£¨example£©µÄ±í¸ñÊDz»ÊǸü¼ÓÈÝÒ×µÄÀí½âµ±Ç°caseµÄÒâͼÁË¡£µ±FeatureºÍExampleÎļþ¶¼Íê³Éºó£¬½èÖúÓÚµÚÈý·½µÄ¿ªÔ´¿ò¼ÜʵÏÖ£¬±ÈÈçCucumber£¬jBehave£¬SpecFlowµÈ°ÑFeatureºÍExample×°»»³É´úÂ룬Ȼºóͨ¹ýµÚ²ã´ÎµÄµ¥Ôª²âÊÔ¿ò¼Ü£¬±ÈÈçJUnit£¬NUnit£¬Spock£¬RSpec£¬½áºÏ²âÊÔÇý¶¯¿ª·¢£¬´Ó¶ø°ÑÒµÎñ´úÂëµÄÂ߼ʵÏÖ¡£ÕæÊÇÒ»¾Ù¶àµÃ¡£
±ÊÕß¾ÍÒÔCucumberºÍJUnitΪÀý£¬¾ÙÒ»¸öBDDµÄÀý×Ó°É¡£´ó¼Ò¶ÔJUnit±È½ÏÊìϤ£¬µ«ÊǶÔCucumber¿ÉÄÜ»áÏà¶ÔİÉúÒ»µã£¬±ÊÕ߾ͻ¨Ò»µã±ÊÄ«£¬¼òµ¥½éÉÜÁËÒ»ÏÂCucumber¡£CucumerÊÇÒ»¸öʵÏÖÁËBDDµÄÒ»¸ö¿ò¼Ü£¬ÆäÖ§³ÖÏÂÃæµÄÓïÑԺͿò¼Ü¼¯³É£¬

ÊDz»ÊǸоõºÜÇ¿´ó°¡£¡£¡£¡£¡ ÏÂÃæ½øÈëʵս£¬
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£©Äã×¼±¸ºÃÁËÂð£¿
|