cucumber-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCucumber 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 item2. 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 saved3. 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 information4. 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
undefinedgherkin
@smoke @critical
Scenario: Login with valid credentials
...
@slow @integration
Scenario: Password reset email workflow
...
@wip
Scenario: OAuth login
# 开发中
...运行指定标签的测试:
bash
undefinedRun 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"
undefinedcucumber --tags "@smoke or @critical"
undefinedWriting 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 successfullyKeep 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 emailAvoid 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 checkoutScenario 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
undefinedDon'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!
undefinedScenario Outline: Multiple features
When I use feature "<feature>"
Then result is "<result>"
Examples:
| feature | result |
| login | success |
| registration | success |
| cart | empty | # 这些是不同的行为!
undefinedKeep 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 orderError 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 unavailabilitygherkin
@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 unavailabilityPerformance
性能优化
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.featurefeatures/
authentication/
login.feature
registration.feature
shopping/
cart.feature
checkout.feature
admin/
user-management.featureCommon 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 bodyTesting 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测试应记录行为、促进协作,并从业务角度确保系统按预期工作。