总结起来就是在说用户写scenarios ,程序员写实现
Refer to
Extreme Programming Explained: Embrace Change [Bec00]
Test Driven Development: By Example [Bec02]
http://behaviour-driven.org/
创建一个cucumber case
引入相关jar
<dependency> <groupId>info.cukes</groupId> <artifactId>cucumber-java8</artifactId> <version>${cucumber.version}</version> </dependency> <dependency> <groupId>info.cukes</groupId> <artifactId>cucumber-junit</artifactId> <version>${cucumber.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>info.cukes</groupId> <artifactId>cucumber-core</artifactId> <version>${cucumber.version}</version> </dependency> <dependency> <groupId>info.cukes</groupId> <artifactId>cucumber-jvm-deps</artifactId> <version>1.0.5</version> </dependency>
最终引出了完整feature的样子,Examples的部分很像Spock里的Data Tables
Feature: Checkout Scenario Outline: Checkout bananas Given the price of a "banana" is 40c When I checkout <count> "banana" Then the total price should be <total>c Examples: | count | total | | 1 | 40 | | 2 | 80 |
feature默认是找同package下的definition去执行
关键字:
Feature Background Scenario Given When Then And But * Scenario Outline Examples
以下引入一个例子
Feature: Feedback when entering invalid credit card details In user testing we've seen a lot of people who made mistakes entering their credit card. We need to be as helpful as possible here to avoid losing users at this crucial stage of the transaction. Background: Given I have chosen some items to buy And I am about to enter my credit card details Scenario: Credit card number too short When I enter a card number that's only 15 digits long And all the other details are correct And I submit the form Then the form should be redisplayed And I should see a message advising me of the correct number of digits Scenario: Expiry date must not be in the past When I enter a card expiry date that's in the past And all the other details are correct And I submit the form Then the form should be redisplayed And I should see a message telling
A Template for Describing a Feature [Feature Injection template (from Chris Matts and Liz Keogh)]
In order to <meet some goal> As a <type of stakeholder> I want <a feature>
原则上,scenario之间,必须无依赖
最基础的样式
Scenario: Successful withdrawal from an account in credit Given I have $100 in my account # the context When I request $20 # the event(s) Then $20 should be dispensed # the outcome(s)
花样写法
Scenario: Attempt withdrawal using stolen card Given I have $100 in my account But my card is invalid When I request $50 Then my card should not be returned And I should be told to contact the bank
or
Scenario: Attempt withdrawal using stolen card Given I have $100 in my account Given my card is invalid When I request $50 Then my card should not be returned Then I should be told to contact the bank
or
Scenario: Attempt withdrawal using stolen card * I have $100 in my account * my card is invalid * I request $50 * my card should not be returned * I should be told to contact the bank
以#开头的行为注视
# This feature covers the account transaction and hardware-driver modules Feature: Withdraw Cash In order to buy beer As an account holder I want to withdraw cash from the ATM # Can't figure out how to integrate with magic wand interface Scenario: Withdraw too much from an account in credit Given I have $50 in my account # When I wave my magic wand And I withdraw $100 Then I should receive $100
传说支持六十多种语言,例如挪威语
# language: no Egenskap: Summering For å unngå at firmaet går konkurs Må regnskapsførerere bruke en regnemaskin for å legge sammen tall Scenario: to tall Gitt at jeg har tastet inn 5 Og at jeg har tastet inn 7 Når jeg summerer Så skal resultatet være 12
ps.一个项目里支持多个不同语言描述的feature,但是因为每一个配置对应一个feature,所以在同一个feature里的scenario都要用同一种语言描述。
如果使用Maven的话,在pom里添加配置,用来跑test case
<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>
默认这么跑Test开头结尾或者TestCase结尾的文件
"**/Test*.java" - includes all of its subdirectories and all java filenames that start with "Test". "**/*Test.java" - includes all of its subdirectories and all java filenames that end with "Test". "**/*TestCase.java" - includes all of its subdirectories and all java filenames that end with "TestCase".
对于Cucumer来说,GIVEN, WHEN, THEN, AND, BUT是同一个东西,都是继承的@StepDefAnnotation
@StepDefAnnotation
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface StepDefAnnotation { }
@Given
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @StepDefAnnotation @Documented public @interface Given { String value(); long timeout() default 0L; }
@When
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @StepDefAnnotation @Documented public @interface When { String value(); long timeout() default 0L; }
@Then
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @StepDefAnnotation @Documented public @interface Then { String value(); long timeout() default 0L; }
@And
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @StepDefAnnotation @Documented public @interface And { String value(); long timeout() default 0L; }
@But
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @StepDefAnnotation @Documented public @interface But { String value(); long timeout() default 0L; }
看起来新增一门语言的实现很简单...
强调了使用各种注解的时候要注意语义语境,不要模棱两可。
最常用的一些正则
\d stands for digit, or [0-9]. \w stands for word character, specifically [A-Za-z0-9_]. Notice that underscores and digits are included but not hyphens. \s stands for whitespace character, specifically [ \t\r\n]. That means a space, a tab, or a line break. \b stands for word boundary, which is a lot like \s but actually means the opposite of \w. Anything that is not a word character is a word boundary.
匹配实例
Scenario: Transfer funds from savings into checking account #1 Given I have deposited $10 in my Checking Account #2 And I have deposited $500 in my Savings Account #3 When I transfer $500 from my Savings Account into my Checking Account #4 Then the balance of the Checking Account should be $510 #5 And the balance of the Savings Account should be $0 #6 But I have deposited $ in my Checking Account #7 But I have deposited $100 in my Checking Accounts #8 But I have deposited $100 in your Checking Account #9 But I have deposited $100 in your Checking Account ANCHORS
@Given("I have deposited \\$(\\d+) in my (\\w+) Account") // + = 1 or more public void iHaveDeposited$InMyAccount(int amount, String accountType) { // TODO: code goes here }// #1	
@Given("I have deposited \\$(\\d*) in my (\\w+) Account") // * = 0 , 1 or more public void iHaveDeposited$InMyAccount(int amount, String accountType) { // TODO: code goes here }// #1	
@Given("I have deposited \\$(\\d+) in my (\\w+) Accounts?") //? = 0 or 1 public void iHaveDeposited$InMyAccount(int amount, String accountType) { // TODO: code goes here }// #1	
@Given("^I have deposited \\$(\\d*) in my (\\w+) Account$") //^ ... $ = specific matching public void iHaveDeposited$InMyAccount(int amount, String accountType) { // TODO: code goes here }// #1
@Given("I have deposited \\$(\\d+) in (?:my|your)my (\\w+) Account") // (?: ... | ...) = or public void iHaveDeposited$InMyAccount(int amount, String accountType) { // TODO: code goes here }// #1	
How Cucumber executes a scenario
Scenario的五种状态
Failed // assert fail or definition error Pending // throw new PendingException() Undefined // cannot match Skipped //the left case after Assert fail or Pending Passed
这段代码体现了一些人可能忽略的地方: 即使是junit这样第三方框架的assert,也是只是抛出了java的AssertionError
import org.junit.*; import static org.junit.Assert.*; public class AssertionExample { public static void main(String[] args) { try { assertTrue(false); } catch (AssertionError e) { System.out.print("Exception was raised was "); System.out.println(e.getClass().getName()); //java.lang.AssertionError } } }
PendingException相当于 TODO
import cucumber.api.java.en.*; import cucumber.api.PendingException; public class Steps { @Given("^I have deposited \\$(\\d+) in my account$") public void iHaveDeposited$InMyAccount(int amount) throws Throwable{ // Write code here that turns the phrase above into concrete actions throw new PendingException(); }
result:
---- T E S T S ------ Running RunCukesTest Feature: Cash Withdrawal Scenario: Successful withdrawal from an account in credit Given I have deposited $100 in my account cucumber.api.PendingException: TODO: implement me at nicebank.Steps.iHaveDeposited$InMyAccount(Steps.java:11) 1 Scenarios (1 undefined) 1 Steps (1 pending)
--strict 用于命令行模式的返回值
两种情况会运行失败
step definition定义有误
assertion不通过