The Cucumber for Java Book讀書筆記[一]

Part 1 Cucumber Fundamentals

總結起來就是在說用戶寫scenarios ,程序員寫實現java

Refer toios

Extreme Programming Explained: Embrace Change [Bec00]git

Test Driven Development: By Example [Bec02]程序員

http://behaviour-driven.org/ web


Part 2 First Taste

建立一個cucumber caseredis

引入相關jarapache

<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 Tablesapi

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

關鍵字:
jvm

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

若是使用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不經過

相關文章
相關標籤/搜索