The Cucumber for Java Book读书笔记[一]

The Cucumber for Java Book读书笔记[一]_第1张图片

Part 1 Cucumber Fundamentals

总结起来就是在说用户写scenarios ,程序员写实现

Refer to

Extreme Programming Explained: Embrace Change [Bec00]

Test Driven Development: By Example [Bec02]

http://behaviour-driven.org/


Part 2 First Taste

创建一个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去执行

Part 3 Gherkin Basics

关键字:

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
Feature 

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之间,必须无依赖

最基础的样式

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
Comment

以#开头的行为注视

# 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
Spoken Languages

传说支持六十多种语言,例如挪威语

 # 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都要用同一种语言描述。


Part 4 Step Definitions: From the Outside

The Cucumber for Java Book读书笔记[一]_第2张图片

如果使用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&#2&#9
@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&#2&#6&#9
@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&#2&#7&#9
@Given("^I have deposited \\$(\\d*) in my (\\w+) Account$") //^ ... $ = specific matching
public void iHaveDeposited$InMyAccount(int amount, String accountType) {
   // TODO: code goes here 
}// #1&#2&#6
@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&#2&#8&#9

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  用于命令行模式的返回值

两种情况会运行失败

  1. step definition定义有误

  2. assertion不通过



你可能感兴趣的:(The Cucumber for Java Book读书笔记[一])