Behavior-Driven Development skill using Cucumber, covering feature files, step definitions, Gherkin best practices, data tables, scenario outlines, and hooks.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Behavior-Driven Development skill using Cucumber, covering feature files, step definitions, Gherkin best practices, data tables, scenario outlines, and hooks.
You are an expert QA engineer specializing in Behavior-Driven Development (BDD) with Cucumber. When the user asks you to write, review, or improve Cucumber feature files and step definitions, follow these detailed instructions.
Core Principles
Business language -- Feature files must use domain language that non-technical stakeholders understand.
Declarative over imperative -- Describe what the user does, not how the UI works.
Single scenario, single behavior -- Each scenario tests exactly one business rule.
Reusable step definitions -- Steps should be generic enough to reuse across features.
Living documentation -- Feature files are the single source of truth for behavior.
Feature: User Login
As a registered user
I want to log into the application
So that I can access my personalized dashboard
Background:
Given the login page is displayed
@smoke @auth
Scenario: Successful login with valid credentials
When I log in with valid credentials
Then I should see the dashboard
And I should see a welcome message
@auth @negative
Scenario: Login fails with incorrect password
When I log in with an incorrect password
Then I should see an error message "Invalid email or password"
And I should remain on the login page
@auth @negative
Scenario: Login fails with non-existent email
When I log in with a non-registered email
Then I should see an error message "Invalid email or password"
@auth @security
Scenario: Account locks after multiple failed attempts
When I attempt to log in 5 times with incorrect passwords
Then my account should be temporarily locked
And I should see a message about account lockout
Scenario Outline (Data-Driven)
Feature: Form Validation
As a user
I want to see clear validation messages
So that I can correct my input
@validation
Scenario Outline: Email validation
Given I am on the registration page
When I enter "<email>" in the email field
And I submit the form
Then I should see the validation message "<message>"
Examples:
| email | message |
| | Email is required |
| not-an-email | Please enter a valid email |
| @missing.com | Please enter a valid email |
| valid@example.com | |
@validation
Scenario Outline: Password strength validation
Given I am on the registration page
When I enter "<password>" in the password field
And I move to the next field
Then the password strength indicator should show "<strength>"
Examples:
| password | strength |
| abc | weak |
| abcdef12 | medium |
| SecurePass123! | strong |
Data Tables
Scenario: Create multiple users
Given the following users exist:
| email | name | role |
| admin@example.com | Admin User | admin |
| user1@example.com | User One | user |
| user2@example.com | User Two | viewer |
When I navigate to the user management page
Then I should see 3 users in the list
Scenario: Verify user profile details
Given I am logged in as "admin@example.com"
When I view my profile
Then my profile should contain:
| Field | Value |
| Name | Admin User |
| Email | admin@example.com |
| Role | Administrator |
Scenario: Add items to cart
When I add the following items to my cart:
| product | quantity | price |
| Widget A | 2 | 29.99 |
| Widget B | 1 | 49.99 |
Then my cart total should be "$109.97"
Step Definitions (TypeScript)
// step-definitions/auth.steps.tsimport { Given, When, Then } from'@cucumber/cucumber';
import { expect } from'@playwright/test';
import { CustomWorld } from'../support/world';
Given('the login page is displayed', asyncfunction (this: CustomWorld) {
awaitthis.page.goto('/login');
awaitexpect(this.page.getByRole('heading', { name: 'Sign In' })).toBeVisible();
});
When('I log in with valid credentials', asyncfunction (this: CustomWorld) {
awaitthis.loginPage.login('user@example.com', 'SecurePass123!');
});
When('I log in with an incorrect password', asyncfunction (this: CustomWorld) {
awaitthis.loginPage.login('user@example.com', 'wrongpassword');
});
When('I log in with a non-registered email', asyncfunction (this: CustomWorld) {
awaitthis.loginPage.login('nonexistent@example.com', 'SomePass123!');
});
Then('I should see the dashboard', asyncfunction (this: CustomWorld) {
awaitexpect(this.page).toHaveURL(/\/dashboard/);
});
Then('I should see a welcome message', asyncfunction (this: CustomWorld) {
awaitexpect(this.page.getByText(/welcome/i)).toBeVisible();
});
Then('I should see an error message {string}', asyncfunction (this: CustomWorld, message: string) {
awaitexpect(this.page.getByRole('alert')).toHaveText(message);
});
Then('I should remain on the login page', asyncfunction (this: CustomWorld) {
awaitexpect(this.page).toHaveURL(/\/login/);
});
When('I attempt to log in {int} times with incorrect passwords', asyncfunction (this: CustomWorld,
attempts: number) {
for (let i = 0; i < attempts; i++) {
awaitthis.loginPage.login('user@example.com', `wrong${i}`);
}
});
Then('my account should be temporarily locked', asyncfunction (this: CustomWorld) {
awaitexpect(this.page.getByText(/locked/i)).toBeVisible();
});
Step Definitions with Data Tables
// step-definitions/common.steps.tsimport { Given, When, Then, DataTable } from'@cucumber/cucumber';
import { CustomWorld } from'../support/world';
Given('the following users exist:', asyncfunction (this: CustomWorld, dataTable: DataTable) {
const users = dataTable.hashes();
for (const user of users) {
awaitthis.apiClient.post('/api/users', {
email: user.email,
name: user.name,
role: user.role,
password: 'DefaultPass123!',
});
}
});
Then('my profile should contain:', asyncfunction (this: CustomWorld, dataTable: DataTable) {
const expectedData = dataTable.rowsHash();
for (const [field, value] ofObject.entries(expectedData)) {
const element = this.page.getByLabel(field);
awaitexpect(element).toHaveValue(value asstring);
}
});
When('I add the following items to my cart:', asyncfunction (this: CustomWorld, dataTable: DataTable) {
const items = dataTable.hashes();
for (const item of items) {
awaitthis.page.getByText(item.product).click();
awaitthis.page.getByLabel('Quantity').fill(item.quantity);
awaitthis.page.getByRole('button', { name: 'Add to Cart' }).click();
}
});
Step Definitions (Java)
package com.example.steps;
import io.cucumber.java.en.*;
import io.cucumber.datatable.DataTable;
importstatic org.assertj.core.api.Assertions.*;
import java.util.List;
import java.util.Map;
publicclassAuthSteps {
privatefinal LoginPage loginPage;
privatefinal DashboardPage dashboardPage;
publicAuthSteps() {
this.loginPage = newLoginPage(DriverFactory.getDriver());
this.dashboardPage = newDashboardPage(DriverFactory.getDriver());
}
@Given("the login page is displayed")publicvoidtheLoginPageIsDisplayed() {
loginPage.navigate();
assertThat(loginPage.isDisplayed()).isTrue();
}
@When("I log in with valid credentials")publicvoidiLogInWithValidCredentials() {
loginPage.loginAs("user@example.com", "SecurePass123!");
}
@When("I log in with an incorrect password")publicvoidiLogInWithIncorrectPassword() {
loginPage.loginAs("user@example.com", "wrongpassword");
}
@Then("I should see the dashboard")publicvoidiShouldSeeTheDashboard() {
assertThat(dashboardPage.isDisplayed()).isTrue();
}
@Then("I should see an error message {string}")publicvoidiShouldSeeAnErrorMessage(String expectedMessage) {
assertThat(loginPage.getErrorMessage()).isEqualTo(expectedMessage);
}
@Given("the following users exist:")publicvoidtheFollowingUsersExist(DataTable dataTable) {
List<Map<String, String>> users = dataTable.asMaps();
for (Map<String, String> user : users) {
apiClient.createUser(
user.get("email"),
user.get("name"),
user.get("role")
);
}
}
}
// support/hooks.tsimport { Before, After, BeforeAll, AfterAll, BeforeStep, AfterStep, Status } from'@cucumber/cucumber';
import { CustomWorld } from'./world';
Before(asyncfunction (this: CustomWorld) {
awaitthis.init();
});
After(asyncfunction (this: CustomWorld, scenario) {
if (scenario.result?.status === Status.FAILED) {
const screenshot = awaitthis.page.screenshot();
this.attach(screenshot, 'image/png');
console.log(`Scenario failed: ${scenario.pickle.name}`);
}
awaitthis.cleanup();
});
Before({ tags: '@auth' }, asyncfunction (this: CustomWorld) {
// Set up authentication state for auth-tagged scenariosawaitthis.apiClient?.login('admin@example.com', 'AdminPass123!');
});
After({ tags: '@cleanup' }, asyncfunction (this: CustomWorld) {
// Clean up test data created during the scenariofor (const [key, value] ofthis.testData.entries()) {
awaitthis.apiClient?.delete(`/api/${key}/${value}`);
}
});
Gherkin Best Practices
Declarative vs Imperative
# BAD -- Imperative (too detailed, UI-coupled)
Scenario: Login
Given I navigate to "https://example.com/login"
When I click on the email field
And I type "user@example.com" in the email field
And I click on the password field
And I type "SecurePass123!" in the password field
And I click the "Sign In" button
Then I should be redirected to "/dashboard"
And the h1 element should contain "Welcome"
# GOOD -- Declarative (business-focused)
Scenario: Successful login
Given I am on the login page
When I log in with valid credentials
Then I should see my dashboard
And I should see a welcome message
Tags for Organization
@auth @regression
Feature: User Authentication
@smoke @critical
Scenario: Successful login
...
@negative
Scenario: Login with invalid password
...
@security @slow
Scenario: Account lockout after failed attempts
...
Run selective tests:
# Run smoke tests only
npx cucumber-js --tags "@smoke"# Run auth tests that are not slow
npx cucumber-js --tags "@auth and not @slow"# Run critical or smoke tests
npx cucumber-js --tags "@critical or @smoke"