cucumber-best-practices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cucumber Best Practices

Cucumber最佳实践

Master patterns and practices for effective Cucumber testing.
掌握高效Cucumber测试的模式与实践。

Scenario Design Principles

场景设计原则

1. Write Declarative Scenarios

1. 编写声明式场景

Focus on what needs to happen, not how it happens.
Imperative (implementation-focused):
gherkin
Scenario: Add product to cart
  Given I navigate to "http://shop.com/products"
  When I find the element with CSS ".product[data-id='123']"
  And I click the button with class "add-to-cart"
  And I wait for the AJAX request to complete
  Then the element ".cart-count" should contain "1"
Declarative (business-focused):
gherkin
Scenario: Add product to cart
  Given I am browsing products
  When I add "Wireless Headphones" to my cart
  Then my cart should contain 1 item
聚焦于需要实现什么,而非如何实现
命令式(侧重实现细节):
gherkin
Scenario: Add product to cart
  Given I navigate to "http://shop.com/products"
  When I find the element with CSS ".product[data-id='123']"
  And I click the button with class "add-to-cart"
  And I wait for the AJAX request to complete
  Then the element ".cart-count" should contain "1"
声明式(侧重业务逻辑):
gherkin
Scenario: Add product to cart
  Given I am browsing products
  When I add "Wireless Headphones" to my cart
  Then my cart should contain 1 item

2. One Scenario, One Behavior

2. 一个场景,一种行为

Each scenario should test exactly one business rule or behavior.
Multiple behaviors in one scenario:
gherkin
Scenario: User registration and login and profile update
  Given I register a new account
  When I log in
  And I update my profile
  And I change my password
  Then everything should work
Separate scenarios:
gherkin
Scenario: Register new account
  When I register with valid details
  Then I should receive a confirmation email

Scenario: Login with new account
  Given I have registered an account
  When I log in with my credentials
  Then I should see my dashboard

Scenario: Update profile
  Given I am logged in
  When I update my profile information
  Then my changes should be saved
每个场景应仅测试一项业务规则或行为。
单场景包含多行为:
gherkin
Scenario: User registration and login and profile update
  Given I register a new account
  When I log in
  And I update my profile
  And I change my password
  Then everything should work
拆分场景:
gherkin
Scenario: Register new account
  When I register with valid details
  Then I should receive a confirmation email

Scenario: Login with new account
  Given I have registered an account
  When I log in with my credentials
  Then I should see my dashboard

Scenario: Update profile
  Given I am logged in
  When I update my profile information
  Then my changes should be saved

3. Keep Scenarios Independent

3. 保持场景独立性

Each scenario should set up its own preconditions.
Dependent scenarios:
gherkin
Scenario: Create order
  When I create order #12345

Scenario: View order
  When I view order #12345  # Depends on previous scenario!
Independent scenarios:
gherkin
Scenario: View order
  Given an order exists with ID "12345"
  When I view the order details
  Then I should see the order information
每个场景应自行设置前置条件。
依赖场景:
gherkin
Scenario: Create order
  When I create order #12345

Scenario: View order
  When I view order #12345  # 依赖前一个场景!
独立场景:
gherkin
Scenario: View order
  Given an order exists with ID "12345"
  When I view the order details
  Then I should see the order information

4. Use Background Wisely

4. 合理使用Background

Use Background for common setup, but don't overuse it.
Good use of Background:
gherkin
Feature: Shopping Cart

  Background:
    Given I am logged in as a customer

  Scenario: Add product to cart
    When I add a product to my cart
    Then my cart should contain 1 item

  Scenario: Remove product from cart
    Given I have a product in my cart
    When I remove the product
    Then my cart should be empty
Background doing too much:
gherkin
Background:
  Given I am on the homepage
  And I click the menu
  And I navigate to products
  And I filter by category "Electronics"
  And I sort by price
  # Too much setup! Not all scenarios need all of this
使用Background处理通用前置条件,但不要过度使用。
Background的正确用法:
gherkin
Feature: Shopping Cart

  Background:
    Given I am logged in as a customer

  Scenario: Add product to cart
    When I add a product to my cart
    Then my cart should contain 1 item

  Scenario: Remove product from cart
    Given I have a product in my cart
    When I remove the product
    Then my cart should be empty
Background过度使用:
gherkin
Background:
  Given I am on the homepage
  And I click the menu
  And I navigate to products
  And I filter by category "Electronics"
  And I sort by price
  # 前置条件过多!并非所有场景都需要这些步骤

Feature Organization

用例组织

Group Related Scenarios

关联场景分组

gherkin
Feature: User Authentication

  Scenario: Successful login
    ...

  Scenario: Failed login with wrong password
    ...

  Scenario: Account lockout after multiple failures
    ...
gherkin
Feature: User Authentication

  Scenario: Successful login
    ...

  Scenario: Failed login with wrong password
    ...

  Scenario: Account lockout after multiple failures
    ...

Use Tags Effectively

高效使用标签

gherkin
@smoke @critical
Scenario: Login with valid credentials
  ...

@slow @integration
Scenario: Password reset email workflow
  ...

@wip
Scenario: OAuth login
  # Work in progress
  ...
Run specific tags:
bash
undefined
gherkin
@smoke @critical
Scenario: Login with valid credentials
  ...

@slow @integration
Scenario: Password reset email workflow
  ...

@wip
Scenario: OAuth login
  # 开发中
  ...
运行指定标签的测试:
bash
undefined

Run smoke tests

运行冒烟测试

cucumber --tags "@smoke"
cucumber --tags "@smoke"

Run all except WIP

运行除开发中以外的所有测试

cucumber --tags "not @wip"
cucumber --tags "not @wip"

Run smoke AND critical

运行同时标记为冒烟和关键的测试

cucumber --tags "@smoke and @critical"
cucumber --tags "@smoke and @critical"

Run smoke OR critical

运行标记为冒烟或关键的测试

cucumber --tags "@smoke or @critical"
undefined
cucumber --tags "@smoke or @critical"
undefined

Writing Good Gherkin

编写优质Gherkin代码

Use Domain Language

使用领域语言

Write in the language of the business domain, not technical terms.
Technical language:
gherkin
Scenario: POST request to /api/users
  When I send a POST to "/api/users" with JSON payload
  And the response status is 201
Domain language:
gherkin
Scenario: Register new user
  When I register a new user account
  Then the user should be created successfully
用业务领域的语言编写,而非技术术语。
技术化语言:
gherkin
Scenario: POST request to /api/users
  When I send a POST to "/api/users" with JSON payload
  And the response status is 201
领域语言:
gherkin
Scenario: Register new user
  When I register a new user account
  Then the user should be created successfully

Keep Steps at the Same Level

保持步骤层级一致

Don't mix high-level and low-level details.
Mixed levels:
gherkin
Scenario: Purchase product
  Given I am logged in
  When I add a product to cart
  And I click the element with ID "checkout-btn"  # Too detailed!
  And I enter credit card "4111111111111111"      # Too detailed!
  Then I complete the purchase
Consistent level:
gherkin
Scenario: Purchase product
  Given I am logged in
  And I have a product in my cart
  When I checkout with a credit card
  Then my order should be completed
  And I should receive a confirmation email
不要混合高层级和低层级细节。
层级混合:
gherkin
Scenario: Purchase product
  Given I am logged in
  When I add a product to cart
  And I click the element with ID "checkout-btn"  # 过于细节!
  And I enter credit card "4111111111111111"      # 过于细节!
  Then I complete the purchase
层级一致:
gherkin
Scenario: Purchase product
  Given I am logged in
  And I have a product in my cart
  When I checkout with a credit card
  Then my order should be completed
  And I should receive a confirmation email

Avoid Conjunctive Steps

避免联合步骤

Don't use "And" to combine multiple distinct actions in prose.
Conjunctive step:
gherkin
When I log in and add a product to cart and checkout
Separate steps:
gherkin
When I log in
And I add a product to my cart
And I proceed to checkout
不要在步骤中用“And”合并多个独立操作。
联合步骤:
gherkin
When I log in and add a product to cart and checkout
拆分步骤:
gherkin
When I log in
And I add a product to my cart
And I proceed to checkout

Scenario Outlines

场景大纲

Use for True Variations

针对真实数据变体使用

Use Scenario Outlines when you need to test the same behavior with different data.
Good use:
gherkin
Scenario Outline: Login validation
  When I log in with "<username>" and "<password>"
  Then I should see "<message>"

  Examples:
    | username | password | message                |
    | valid    | valid    | Welcome                |
    | invalid  | valid    | Invalid username       |
    | valid    | invalid  | Invalid password       |
    | empty    | empty    | Username required      |
Overusing Scenario Outline:
gherkin
undefined
当需要用不同数据测试同一行为时,使用场景大纲。
正确用法:
gherkin
Scenario Outline: Login validation
  When I log in with "<username>" and "<password>"
  Then I should see "<message>"

  Examples:
    | username | password | message                |
    | valid    | valid    | Welcome                |
    | invalid  | valid    | Invalid username       |
    | valid    | invalid  | Invalid password       |
    | empty    | empty    | Username required      |
过度使用场景大纲:
gherkin
undefined

Don't use Scenario Outline for unrelated test cases

不要为不相关的测试用例使用场景大纲

Scenario Outline: Multiple features When I use feature "<feature>" Then result is "<result>"
Examples: | feature | result | | login | success | | registration | success | | cart | empty | # These are different behaviors!
undefined
Scenario Outline: Multiple features When I use feature "<feature>" Then result is "<result>"
Examples: | feature | result | | login | success | | registration | success | | cart | empty | # 这些是不同的行为!
undefined

Keep Examples Meaningful

保持示例有意义

gherkin
Scenario Outline: Discount calculation
  Given a customer with "<membership>" status
  When they purchase items totaling $<amount>
  Then they should receive a $<discount> discount

  Examples: Standard discounts
    | membership | amount | discount |
    | silver     | 100    | 5        |
    | gold       | 100    | 10       |
    | platinum   | 100    | 15       |

  Examples: Minimum purchase thresholds
    | membership | amount | discount |
    | silver     | 49     | 0        |
    | silver     | 50     | 2.50     |
gherkin
Scenario Outline: Discount calculation
  Given a customer with "<membership>" status
  When they purchase items totaling $<amount>
  Then they should receive a $<discount> discount

  Examples: Standard discounts
    | membership | amount | discount |
    | silver     | 100    | 5        |
    | gold       | 100    | 10       |
    | platinum   | 100    | 15       |

  Examples: Minimum purchase thresholds
    | membership | amount | discount |
    | silver     | 49     | 0        |
    | silver     | 50     | 2.50     |

Step Definition Patterns

步骤定义模式

Create Reusable Steps

创建可复用步骤

javascript
// Generic, reusable
When('I fill in {string} with {string}', async function(field, value) {
  await this.page.fill(`[name="${field}"]`, value);
});

// Used in multiple scenarios:
When('I fill in "email" with "test@example.com"')
When('I fill in "password" with "secure123"')
When('I fill in "search" with "products"')
javascript
// 通用可复用步骤
When('I fill in {string} with {string}', async function(field, value) {
  await this.page.fill(`[name="${field}"]`, value);
});

// 在多个场景中使用:
When('I fill in "email" with "test@example.com"')
When('I fill in "password" with "secure123"')
When('I fill in "search" with "products"')

Avoid Over-Generic Steps

避免过度通用的步骤

Balance reusability with readability.
Too generic:
javascript
When('I do {string} with {string} and {string}', ...)
Specific and readable:
javascript
When('I log in with {string} and {string}', ...)
When('I search for {string} in {string}', ...)
在可复用性和可读性之间取得平衡。
过度通用:
javascript
When('I do {string} with {string} and {string}', ...)
具体且可读:
javascript
When('I log in with {string} and {string}', ...)
When('I search for {string} in {string}', ...)

Data Management

数据管理

Use Factories for Test Data

使用工厂类生成测试数据

javascript
// support/factories.js
const faker = require('faker');

class UserFactory {
  static create(overrides = {}) {
    return {
      firstName: faker.name.firstName(),
      lastName: faker.name.lastName(),
      email: faker.internet.email(),
      password: 'Test123!',
      ...overrides
    };
  }
}

// Use in steps
Given('I register a new user', async function() {
  const user = UserFactory.create();
  this.currentUser = user;
  await this.api.register(user);
});
javascript
// support/factories.js
const faker = require('faker');

class UserFactory {
  static create(overrides = {}) {
    return {
      firstName: faker.name.firstName(),
      lastName: faker.name.lastName(),
      email: faker.internet.email(),
      password: 'Test123!',
      ...overrides
    };
  }
}

// 在步骤中使用
Given('I register a new user', async function() {
  const user = UserFactory.create();
  this.currentUser = user;
  await this.api.register(user);
});

Avoid Hardcoded IDs

避免硬编码ID

Hardcoded:
gherkin
Given user "12345" exists
When I view order "67890"
Named entities:
gherkin
Given a user "john@example.com" exists
When I view my most recent order
硬编码:
gherkin
Given user "12345" exists
When I view order "67890"
命名实体:
gherkin
Given a user "john@example.com" exists
When I view my most recent order

Error Handling

错误处理

Test Happy and Unhappy Paths

测试正常与异常流程

gherkin
@happy-path
Scenario: Successful checkout
  Given I have items in my cart
  When I complete the checkout process
  Then my order should be confirmed

@error-handling
Scenario: Checkout with expired card
  Given I have items in my cart
  When I checkout with an expired credit card
  Then I should see an error message
  And my order should not be processed

@edge-case
Scenario: Checkout with insufficient inventory
  Given I have a product in my cart
  But the product is out of stock
  When I attempt to checkout
  Then I should be notified about stock unavailability
gherkin
@happy-path
Scenario: Successful checkout
  Given I have items in my cart
  When I complete the checkout process
  Then my order should be confirmed

@error-handling
Scenario: Checkout with expired card
  Given I have items in my cart
  When I checkout with an expired credit card
  Then I should see an error message
  And my order should not be processed

@edge-case
Scenario: Checkout with insufficient inventory
  Given I have a product in my cart
  But the product is out of stock
  When I attempt to checkout
  Then I should be notified about stock unavailability

Performance

性能优化

Tag Slow Tests

标记慢测试

gherkin
@slow @integration
Scenario: Full order workflow with email notifications
  # Takes 30 seconds to run
  ...
gherkin
@slow @integration
Scenario: Full order workflow with email notifications
  # 运行需要30秒
  ...

Parallel Execution

并行执行

Ensure scenarios can run in parallel:
javascript
// cucumber.js
module.exports = {
  default: '--parallel 4'
};
确保场景可以并行运行:
javascript
// cucumber.js
module.exports = {
  default: '--parallel 4'
};

Maintenance

维护建议

Regular Review

定期审查

  • Remove obsolete scenarios
  • Update scenarios when requirements change
  • Refactor duplicate steps
  • Keep features organized
  • 删除过时场景
  • 当需求变更时更新场景
  • 重构重复步骤
  • 保持用例组织有序

Version Control

版本控制

features/
  authentication/
    login.feature
    registration.feature
  shopping/
    cart.feature
    checkout.feature
  admin/
    user-management.feature
features/
  authentication/
    login.feature
    registration.feature
  shopping/
    cart.feature
    checkout.feature
  admin/
    user-management.feature

Common Anti-Patterns

常见反模式

Testing implementation details:
gherkin
Then the database should have 1 record in the users table
UI-specific assertions in business scenarios:
gherkin
Then I should see a red error message in the top right corner
Using Given for actions:
gherkin
Given I click the submit button  # This is a When, not a Given!
Technical jargon:
gherkin
When I POST to /api/v1/users with JSON body
测试实现细节:
gherkin
Then the database should have 1 record in the users table
业务场景中包含UI特定断言:
gherkin
Then I should see a red error message in the top right corner
用Given执行操作:
gherkin
Given I click the submit button  # 这应该是When步骤,而非Given!
使用技术术语:
gherkin
When I POST to /api/v1/users with JSON body

Testing Pyramid

测试金字塔

Use Cucumber appropriately in your test strategy:
  • E2E Cucumber Tests: Critical user journeys (20%)
  • Integration Tests: API/service interactions (30%)
  • Unit Tests: Business logic (50%)
Don't try to test everything with Cucumber. Use it for high-value acceptance tests.
Remember: Cucumber tests should document behavior, facilitate collaboration, and provide confidence that the system works as expected from a business perspective.
在测试策略中合理使用Cucumber:
  • 端到端Cucumber测试:核心用户流程(20%)
  • 集成测试:API/服务交互(30%)
  • 单元测试:业务逻辑(50%)
不要试图用Cucumber测试所有内容。将其用于高价值的验收测试。
记住:Cucumber测试应记录行为、促进协作,并从业务角度确保系统按预期工作。