cucumber-gherkin

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cucumber & Gherkin Skill

Cucumber & Gherkin 技能指南

BDD testing framework with plain-text executable specifications. Gherkin syntax with step definitions in Ruby, JavaScript, Java, or Python.
基于纯文本可执行规格说明的BDD测试框架。支持Ruby、JavaScript、Java或Python语言的步骤定义与Gherkin语法。

Core Concepts

核心概念

Cucumber reads executable specifications in plain text and validates software behavior. Gherkin is the structured grammar making plain text machine-readable.
┌────────────┐                 ┌──────────────┐                 ┌───────────┐
│   Steps    │                 │     Step     │                 │           │
│ in Gherkin ├──matched with──>│ Definitions  ├───manipulates──>│  System   │
└────────────┘                 └──────────────┘                 └───────────┘
Cucumber 读取纯文本格式的可执行规格说明,并验证软件行为。Gherkin 是一种结构化语法,让纯文本内容能够被机器识别。
┌────────────┐                 ┌──────────────┐                 ┌───────────┐
│   Steps    │                 │     Step     │                 │           │
│ in Gherkin ├──matched with──>│ Definitions  ├───manipulates──>│  System   │
└────────────┘                 └──────────────┘                 └───────────┘

Gherkin Syntax Quick Reference

Gherkin语法速查

Primary Keywords

核心关键字

gherkin
Feature: Short description
  Optional multi-line description explaining the feature.
  
  Background:
    Given common setup steps for all scenarios
  
  Rule: Business rule grouping (Gherkin 6+)
    
    Scenario: Concrete example illustrating the rule
      Given an initial context (past tense, setup)
      When an action occurs (present tense, trigger)
      Then expected outcome (assertion)
      And additional step
      But negative assertion
    
    Scenario Outline: Parameterized scenario
      Given there are <start> items
      When I remove <remove> items
      Then I should have <remaining> items
      
      Examples:
        | start | remove | remaining |
        |    12 |      5 |         7 |
        |    20 |      5 |        15 |
gherkin
Feature: Short description
  Optional multi-line description explaining the feature.
  
  Background:
    Given common setup steps for all scenarios
  
  Rule: Business rule grouping (Gherkin 6+)
    
    Scenario: Concrete example illustrating the rule
      Given an initial context (past tense, setup)
      When an action occurs (present tense, trigger)
      Then expected outcome (assertion)
      And additional step
      But negative assertion
    
    Scenario Outline: Parameterized scenario
      Given there are <start> items
      When I remove <remove> items
      Then I should have <remaining> items
      
      Examples:
        | start | remove | remaining |
        |    12 |      5 |         7 |
        |    20 |      5 |        15 |

Step Keywords

步骤关键字

KeywordPurposeExample
Given
Setup/precondition
Given I am logged in as "admin"
When
Action/trigger
When I click the submit button
Then
Assertion/outcome
Then I should see "Success"
And
Continue previous type
And I have 3 items in my cart
But
Negative continuation
But I should not see "Error"
*
Bullet-style step
* I have eggs
关键字用途示例
Given
初始化/前置条件
Given I am logged in as "admin"
When
动作/触发事件
When I click the submit button
Then
断言/预期结果
Then I should see "Success"
And
延续同类型步骤
And I have 3 items in my cart
But
反向延续步骤
But I should not see "Error"
*
无序列表式步骤
* I have eggs

Data Structures

数据结构

Data Tables - tabular data:
gherkin
Given the following users exist:
  | name   | email              | role  |
  | Alice  | alice@example.com  | admin |
  | Bob    | bob@example.com    | user  |
Doc Strings - multi-line text:
gherkin
Given a blog post with content:
  """markdown
  # My Post Title
  
  This is the content of my blog post.
  """
数据表 - 表格形式的数据:
gherkin
Given the following users exist:
  | name   | email              | role  |
  | Alice  | alice@example.com  | admin |
  | Bob    | bob@example.com    | user  |
文档字符串 - 多行文本:
gherkin
Given a blog post with content:
  """markdown
  # My Post Title
  
  This is the content of my blog post.
  """

Tags

标签

gherkin
@smoke @critical
Feature: User authentication

  @wip
  Scenario: Login with valid credentials
    ...
  
  @slow @database
  Scenario: Bulk user import
    ...
Tag expressions:
@smoke and not @slow
,
@gui or @api
,
(@smoke or @critical) and not @wip
gherkin
@smoke @critical
Feature: User authentication

  @wip
  Scenario: Login with valid credentials
    ...
  
  @slow @database
  Scenario: Bulk user import
    ...
标签表达式:
@smoke and not @slow
@gui or @api
(@smoke or @critical) and not @wip

Step Definitions

步骤定义

Match Gherkin steps to code. Use Cucumber Expressions (preferred) or Regular Expressions.
将Gherkin步骤与代码关联。推荐使用Cucumber表达式,也支持正则表达式。

Ruby

Ruby

ruby
Given('I have {int} cucumbers in my belly') do |count|
  @belly = Belly.new
  @belly.eat(count)
end

When('I wait {int} hour(s)') do |hours|
  @belly.wait(hours)
end

Then('my belly should growl') do
  expect(@belly.growling?).to be true
end
ruby
Given('I have {int} cucumbers in my belly') do |count|
  @belly = Belly.new
  @belly.eat(count)
end

When('I wait {int} hour(s)') do |hours|
  @belly.wait(hours)
end

Then('my belly should growl') do
  expect(@belly.growling?).to be true
end

With data table

With data table

Given('the following users exist:') do |table| table.hashes.each do |row| User.create!(row) end end
undefined
Given('the following users exist:') do |table| table.hashes.each do |row| User.create!(row) end end
undefined

JavaScript

JavaScript

javascript
const { Given, When, Then } = require('@cucumber/cucumber');

Given('I have {int} cucumbers in my belly', function(count) {
  this.belly = new Belly();
  this.belly.eat(count);
});

When('I wait {int} hour(s)', async function(hours) {
  await this.belly.wait(hours);
});

Then('my belly should growl', function() {
  expect(this.belly.isGrowling()).toBe(true);
});

// With data table
Given('the following users exist:', async function(dataTable) {
  for (const row of dataTable.hashes()) {
    await User.create(row);
  }
});
javascript
const { Given, When, Then } = require('@cucumber/cucumber');

Given('I have {int} cucumbers in my belly', function(count) {
  this.belly = new Belly();
  this.belly.eat(count);
});

When('I wait {int} hour(s)', async function(hours) {
  await this.belly.wait(hours);
});

Then('my belly should growl', function() {
  expect(this.belly.isGrowling()).toBe(true);
});

// With data table
Given('the following users exist:', async function(dataTable) {
  for (const row of dataTable.hashes()) {
    await User.create(row);
  }
});

Java

Java

java
public class StepDefinitions {
    @Given("I have {int} cucumbers in my belly")
    public void iHaveCucumbersInMyBelly(int count) {
        belly = new Belly();
        belly.eat(count);
    }
    
    @When("I wait {int} hour(s)")
    public void iWaitHours(int hours) {
        belly.wait(hours);
    }
    
    @Then("my belly should growl")
    public void myBellyShouldGrowl() {
        assertTrue(belly.isGrowling());
    }
}
java
public class StepDefinitions {
    @Given("I have {int} cucumbers in my belly")
    public void iHaveCucumbersInMyBelly(int count) {
        belly = new Belly();
        belly.eat(count);
    }
    
    @When("I wait {int} hour(s)")
    public void iWaitHours(int hours) {
        belly.wait(hours);
    }
    
    @Then("my belly should growl")
    public void myBellyShouldGrowl() {
        assertTrue(belly.isGrowling());
    }
}

Cucumber Expressions

Cucumber表达式

Built-in parameter types:
{int}
,
{float}
,
{word}
,
{string}
,
{}
(anonymous)
Optional text:
cucumber(s)
matches "cucumber" or "cucumbers" Alternative text:
color/colour
matches "color" or "colour"
内置参数类型:
{int}
{float}
{word}
{string}
{}
(匿名)
可选文本:
cucumber(s)
匹配 "cucumber" 或 "cucumbers" 备选文本:
color/colour
匹配 "color" 或 "colour"

Hooks

钩子

Scenario Hooks

场景钩子

ruby
undefined
ruby
undefined

Ruby

Ruby

Before do |scenario|

runs before each scenario

end
After do |scenario|

runs after each scenario

screenshot if scenario.failed? end

```javascript
// JavaScript
const { Before, After } = require('@cucumber/cucumber');

Before(async function(scenario) {
  // runs before each scenario
});

After(async function(scenario) {
  // runs after each scenario
  if (scenario.result.status === 'FAILED') {
    await this.screenshot();
  }
});
Before do |scenario|

runs before each scenario

end
After do |scenario|

runs after each scenario

screenshot if scenario.failed? end

```javascript
// JavaScript
const { Before, After } = require('@cucumber/cucumber');

Before(async function(scenario) {
  // runs before each scenario
});

After(async function(scenario) {
  // runs after each scenario
  if (scenario.result.status === 'FAILED') {
    await this.screenshot();
  }
});

Conditional Hooks (with tags)

条件钩子(带标签)

ruby
Before('@database') do
  DatabaseCleaner.start
end

After('@database') do
  DatabaseCleaner.clean
end
javascript
Before({ tags: '@browser and not @headless' }, async function() {
  this.browser = await launchBrowser();
});
ruby
Before('@database') do
  DatabaseCleaner.start
end

After('@database') do
  DatabaseCleaner.clean
end
javascript
Before({ tags: '@browser and not @headless' }, async function() {
  this.browser = await launchBrowser();
});

Global Hooks

全局钩子

ruby
BeforeAll do
  # once before any scenario
end

AfterAll do
  # once after all scenarios
end
ruby
BeforeAll do
  # once before any scenario
end

AfterAll do
  # once after all scenarios
end

Best Practices

最佳实践

Declarative over Imperative

声明式优于命令式

Good (declarative):
gherkin
When "Bob" logs in
Then he sees his dashboard
Avoid (imperative):
gherkin
When I visit "/login"
And I enter "bob" in "username"
And I enter "secret" in "password"
And I click "Login"
Then I should see "Dashboard"
推荐(声明式):
gherkin
When "Bob" logs in
Then he sees his dashboard
避免(命令式):
gherkin
When I visit "/login"
And I enter "bob" in "username"
And I enter "secret" in "password"
And I click "Login"
Then I should see "Dashboard"

Focus on Behavior, Not Implementation

聚焦行为,而非实现

  • Describe what the system does, not how
  • Use domain language stakeholders understand
  • Keep scenarios short (3-5 steps recommended)
  • One behavior per scenario
  • 描述系统做什么,而非怎么做
  • 使用利益相关者能理解的领域语言
  • 保持场景简短(建议3-5个步骤)
  • 每个场景对应一个行为

Background Usage

Background的使用

  • Keep backgrounds short (≤4 lines)
  • Use only for essential shared context
  • Move implementation details to step definitions
  • 保持Background简短(≤4行)
  • 仅用于必要的共享上下文
  • 将实现细节移至步骤定义中

Running Cucumber

运行Cucumber

bash
undefined
bash
undefined

Ruby

Ruby

bundle exec cucumber cucumber --tags "@smoke and not @wip" cucumber features/login.feature:10 # specific line
bundle exec cucumber cucumber --tags "@smoke and not @wip" cucumber features/login.feature:10 # specific line

JavaScript

JavaScript

npx cucumber-js npx cucumber-js --tags "@smoke"
npx cucumber-js npx cucumber-js --tags "@smoke"

Java (with Maven)

Java (with Maven)

mvn test -Dcucumber.filter.tags="@smoke"
undefined
mvn test -Dcucumber.filter.tags="@smoke"
undefined

Additional References

更多参考资料

For comprehensive details, see reference files:
  • references/gherkin-syntax.md
    - Complete Gherkin language reference
  • references/step-definitions.md
    - Step definition patterns by language
  • references/hooks-config.md
    - Hooks, configuration, and runners
  • references/best-practices.md
    - Anti-patterns and advanced patterns
如需详细内容,请查看参考文件:
  • references/gherkin-syntax.md
    - 完整Gherkin语言参考
  • references/step-definitions.md
    - 各语言的步骤定义模式
  • references/hooks-config.md
    - 钩子、配置及运行器
  • references/best-practices.md
    - 反模式与进阶模式