gherkin-from-stories
Converts requirements in any input shape into Gherkin scenarios - a user story ("As a … I want … so that …"), a signed-off acceptance-criteria list (ATDD: @AC-N-tagged scenarios, NotImplementedError step stubs, AC-to-test traceability table), existing manual test steps (declarative rewrite that strips UI mechanics), or a raw spec / PRD section (acceptance-criteria extraction with Gherkin or plain-list output). Maps criteria to Scenario blocks, detects Scenario Outline opportunities, factors shared Background, reuses the curated step library, and flags implicit preconditions instead of fabricating them. Emits Gherkin (plus stubs in ATDD mode): runner detection and full step wiring belong to bdd-scenario-author. Use whenever requirements text of any shape needs to become a .feature file.
Install with skills.sh (any agent)
npx skills add testland/qa --skill gherkin-from-storiesgherkin-from-stories
Overview
The shift-left flow:
Spec / User story → Acceptance Criteria → Gherkin Feature → Step definitions → TestsThis skill is the authoring umbrella for the left half of that flow: it turns requirements text into a .feature file. Four input shapes route to the same core transform:
| Input you have | Section to use | Extra output |
|---|---|---|
| A user story with the As-a / I-want / So-that triple | "From a user story" | - |
| A signed-off, numbered acceptance-criteria list | "From an acceptance-criteria list (ATDD)" | @AC-N tags, step stubs, traceability table |
| Already-written manual test steps (TestRail / Qase / Xray export, prose script) | "From manual test steps" | side-by-side rewrite table |
| A raw spec / PRD section with no AC structure yet | "Extracting acceptance criteria from a raw spec" | Gherkin or plain numbered AC list |
Whatever the input, the same core discipline applies: one Scenario per behavior, Scenario Outline when only data varies, shared Background, step library reuse, and flag-and-ask on implicit preconditions.
When to use
Not this skill: reviewing existing Gherkin for style (use gherkin-style-reviewer); wiring step definitions to a runner end to end (use bdd-scenario-author, which invokes this skill first); non-functional requirements - perf / a11y / security thresholds have a different shape (thresholds, not Given/When/Then) - use non-functional-requirement-extractor in the qa-shift-left plugin.
From a user story
Step 1 - Extract the user-story triple
# Story: Apply promo code at checkout
**As a** logged-in customer
**I want** to apply a promotional code at checkout
**So that** I receive the advertised discount on my orderThe triple maps to the Feature header:
Feature: Apply promo code at checkout
As a logged-in customer
I want to apply a promotional code at checkout
So that I receive the advertised discount on my orderIf the story doesn't have the triple, flag and ask - a story without explicit value is a signal the team should clarify before testing.
Step 2 - Map acceptance criteria to Scenarios
The story body usually has an AC list; each AC becomes a Scenario:
Background:
Given I am a logged-in customer
And my cart contains 1 item at $24.99
Scenario: Apply valid promo
Given promo code "WELCOME10" is active
When I enter "WELCOME10" in the promo input
And I click "Apply"
Then the subtotal updates to $22.49
And a confirmation toast appears: "Code applied"
Scenario: Apply expired promo
Given promo code "EXPIRED50" is inactive
When I enter "EXPIRED50" in the promo input
And I click "Apply"
Then an error appears: "This code has expired"Step 3 - Identify Scenario Outline opportunities
Multiple ACs that vary only in input data become a Scenario Outline - use one whenever the underlying logic is identical and only the data varies:
Scenario Outline: Promo validation rejects bad input
When I enter "<code>" in the promo input
And I click "Apply"
Then an error appears: "<error>"
Examples:
| code | error |
| EXPIRED50 | This code has expired |
| NOTREAL | Code not found |
| (empty) | Please enter a code |
| WELCOME10*2 | Already applied |From an acceptance-criteria list (ATDD)
When the input is a signed-off, numbered AC list and the team gates implementation on green acceptance tests (per ISTQB, ATDD is "a collaboration-based test-first approach that defines acceptance tests in the stakeholders' domain language"):
The full worked feature, stub examples, traceability artifact, per-runner tag-filter commands, and ATDD-specific anti-patterns: references/atdd-traceability.md.
From manual test steps
When the input is an already-written manual step (table row, prose bullet, TestRail / Qase / Xray exported step), the job is a declarative rewrite, not a translation: strip UI mechanics ("clicks the button", "types in the field"), elevate user intent ("signs in", "adds the product"), and align vocabulary with the existing step library.
The classification table, the full R1-R5 rule catalog with examples, and migration-specific anti-patterns: references/manual-step-rules.md.
Extracting acceptance criteria from a raw spec
When the input is a PRD section or feature spec with no AC structure yet, extract the criteria first, then feed them through the sections above. Emits two interchangeable shapes: Gherkin (for Cucumber / Behave / Reqnroll / pytest-bdd projects) or a plain numbered list (AC-1, AC-2, … - consumable by the ATDD section and usable as commit-message references, e.g. feat: AC-3 - show toast on save).
Three worked examples (simple story, PRD with implicit preconditions, Scenario Outline opportunity): references/spec-extraction-examples.md.
Shared discipline (all input shapes)
Use existing steps from the library
Per bdd-step-library-curator, the team has a curated step library. Use existing steps where possible:
# Use existing step:
Given I am a logged-in customer
# vs (avoid):
Given I have authenticated to the system # NEW STEP - duplicates "I am a logged-in customer"Before authoring a new step, search the library README.
Flag implicit Givens
Requirements often imply preconditions. Flag instead of guess:
## ⚠ Implicit Given flags (3)
1. Where does the user enter the promo? `/checkout`? `/cart`?
2. What's the cart state? Empty? Multi-item?
3. Authentication required? Guest checkout supported?
The Gherkin Feature can't be authored without these answers.Flag-and-ask is the load-bearing pattern: silently picking one reading produces a test suite that misses the paths the author never confirmed.
Validate Gherkin style
Output
## Gherkin scenarios for `<source>`
**Source:** `LIN-1234` (story) | AC list | manual script | PRD section
**Implicit-precondition flags:** N
**Scenarios produced:** M
**Step library reuse:** K of M scenarios use existing steps only.
### Generated Feature
### Implicit-precondition flags
### New steps required
| Step | Why new |
|-----------------------------------------------|---------|
| `Given promo code {code} is active` | New domain (admin promo state) |
### Recommended next step
After the PM clarifies flagged Givens, author the new step definitions per
`bdd-step-library-curator` conventions and pair with the team's runner
(`cucumber-testing` / `behave-testing` / `reqnroll-testing`), or hand the
whole flow to `bdd-scenario-author`.Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Fabricating implicit Givens | Tests pass for the wrong reason; the author never confirmed | Flag-and-ask |
| One Scenario per AC even when they should be an Outline | Test code duplication | Detect outline opportunities |
| Not consulting the step library | Step proliferation; library bloats | Search library first |
| Imperative steps ("click button #foo") | Couples to UI; defeats BDD's value | Declarative ("I apply a promo") |
| Skipping the As-a / I-want / So-that header | Loses the value framing | Triple at the Feature top |
| Then with a verb but no observable target ("Then save") | Not testable | Emit data-testid / response status / DOM state |
| Copy-pasted Givens across scenarios | Background extraction missed; brittle suite | One Background block for shared state |
Limitations
References
ATDD from an acceptance-criteria list - tags, stubs, traceability
View source (opens in new window)ATDD from an acceptance-criteria list - tags, stubs, traceability
Deep reference for gherkin-from-stories ("from an acceptance-criteria list" input shape). Consult when the team practices ATDD and needs the full tagged-scenario, red-first, traceability workflow.
Per ISTQB Glossary V4.7.1, ATDD (Acceptance Test-Driven Development) is "a collaboration-based test-first approach that defines acceptance tests in the stakeholders' domain language" (https://glossary.istqb.org/en_US/term/acceptance-test-driven-development). The flow: Story → AC → Acceptance test → Implementation → Pass. The acceptance test is written first; the implementation follows; success = test passes.
One tagged test per AC
Input AC list (each AC has an ID - preserved end-to-end so test failures map back):
## Acceptance criteria
- AC-1.1: Valid promo "WELCOME10" reduces subtotal by 10%.
- AC-1.2: Expired promo shows the message "This code has expired."
- AC-1.3: Empty promo input shows "Please enter a code."
- AC-1.4: Already-applied promo shows "Already applied" without re-applying.
- AC-1.5: Promo applies before tax (per current pricing logic).# Features/promo-application.feature
Feature: Apply promo code at checkout
Background:
Given a logged-in customer
And the cart contains 1 of "BOOK-001" at $24.99
@AC-1.1
Scenario: Valid promo reduces subtotal
Given promo code "WELCOME10" is active
When I enter "WELCOME10" in the promo input
And I click "Apply"
Then the subtotal updates to $22.49
@AC-1.2
Scenario: Expired promo shows error
Given promo code "EXPIRED50" is inactive
When I enter "EXPIRED50" in the promo input
And I click "Apply"
Then an error appears: "This code has expired"
@AC-1.3
Scenario: Empty promo input
When I enter "" in the promo input
And I click "Apply"
Then an error appears: "Please enter a code"
@AC-1.4
Scenario: Already-applied promo
Given promo code "WELCOME10" is active
And I have already applied "WELCOME10"
When I enter "WELCOME10" in the promo input
And I click "Apply"
Then an error appears: "Already applied"
And the subtotal remains $22.49
@AC-1.5
Scenario: Promo applies before tax
Given the tax rate for this region is 10%
And promo code "WELCOME10" is active
When I enter "WELCOME10" in the promo input
And I click "Apply"
Then the subtotal updates to $22.49
And the tax updates to $2.249 (10% of $22.49)
And the total is $24.74The @AC-X.Y tag is the load-bearing traceability: failing tests report which AC failed.
Initial state - all tests fail
Per ATDD, tests are written before implementation. Initial run:
Scenario: Valid promo reduces subtotal
Given promo code "WELCOME10" is active # PASS (admin seeding works)
When I enter "WELCOME10" in the promo input # PASS (input field exists)
And I click "Apply" # PASS (button exists)
Then the subtotal updates to $22.49 # FAIL - promo logic not implemented
5 of 5 scenarios FAILED (as expected - implementation pending).The failing tests are the work backlog. Implementation drives them green one by one; the story is "done" only when all AC tests pass - per the team's DoD (definition-of-done in the qa-process plugin).
Scaffold new step definitions
Detect undefined steps and emit stub definitions whose bodies raise:
# features/steps/promo_steps.py
@given('promo code "{code}" is active')
def step_promo_active(context, code):
raise NotImplementedError(f"Implement: seed promo {code} as active")
@when('I enter "{code}" in the promo input')
def step_enter_promo(context, code):
raise NotImplementedError(f"Implement: type {code} in promo input")
@then('an error appears: "{message}"')
def step_error_appears(context, message):
raise NotImplementedError(f"Implement: assert error message {message}")
@then('the subtotal updates to ${expected:f}')
def step_subtotal(context, expected):
raise NotImplementedError(f"Implement: assert subtotal == {expected}")The NotImplementedError body makes the test failure helpful - the engineer knows exactly what to implement. Auto-generated step bodies that pass silently are the cardinal ATDD sin: tests appear green while production code never runs.
Traceability artifact
## AC-to-test mapping - `<story>` (auto-generated)
| AC ID | Test | Status | Last run |
|--------|--------------------------------------------------|---------|----------|
| AC-1.1 | `promo-application.feature:8` (Valid promo) | pass | 2026-05-05 |
| AC-1.2 | `promo-application.feature:14` (Expired) | pass | 2026-05-05 |
| AC-1.3 | `promo-application.feature:20` (Empty) | pass | 2026-05-05 |
| AC-1.4 | `promo-application.feature:25` (Already applied) | pass | 2026-05-05 |
| AC-1.5 | `promo-application.feature:32` (Tax interaction) | pass | 2026-05-05 |
**Coverage:** 5/5 AC covered by tests. Story is testable per ATDD.This artifact answers "Did we test what the customer asked for?" - a 1:1 mapping of AC → test → status answers it definitively, and closes the compliance / audit gap.
Run via the team's framework
Default: match the team's existing BDD runner - ATDD lives or dies by adoption, and forcing a runner switch alongside test-first authoring stalls both. Use the incumbent runner (Cucumber-JVM, Behave, or Reqnroll). Pick a new runner only when no incumbent exists; then default to the production stack's primary language (JVM → Cucumber-JVM, Python → Behave, .NET → Reqnroll).
# Cucumber-JVM
mvn test -Dcucumber.filter.tags='@AC-1.1 or @AC-1.2 or @AC-1.3 or @AC-1.4 or @AC-1.5'
# Behave
behave --tags=@AC-1.1 --tags=@AC-1.2 --tags=@AC-1.3 --tags=@AC-1.4 --tags=@AC-1.5
# Reqnroll
dotnet test --filter "Category=AC-1.1|Category=AC-1.2|..."ATDD-specific anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Tests written after implementation | Defeats ATDD; tests verify what was built, not what was asked | Test-first; tests precede implementation |
Tests without @AC-X.Y tag | No traceability; story acceptance unverifiable | Tag every Scenario |
| One mega-scenario covering all ACs | Failure mid-scenario doesn't pinpoint which AC failed | One Scenario per AC |
| Auto-generated step bodies that pass silently | Tests appear green; production code never runs | NotImplementedError stubs |
| Skipping the AC-to-test artifact | Compliance / audit gap | Generate the mapping each release |
| AC tests in the same suite as unit tests | Mixed feedback; harder to interpret | Separate features/acceptance/ suite |
Manual test steps to declarative Gherkin - classification and rewrite rules
View source (opens in new window)Manual test steps to declarative Gherkin - classification and rewrite rules
Deep reference for gherkin-from-stories ("from manual test steps" input shape). Consult when migrating manual scripts (TestRail / Qase / Xray exports) to BDD or handing a manual script to an automation engineer.
A manual test script and a Gherkin scenario describe the same behavior at different abstraction levels. Manual scripts are imperative ("click the Add to Cart button, then verify the cart count is 1"); Gherkin steps should be declarative ("the customer adds a product to their cart, then their cart shows one item"). The conversion goes wrong in a predictable way: one-for-one translation of UI mechanics produces brittle, implementation-coupled scenarios that Cucumber's own guidance warns against (https://cucumber.io/docs/bdd/better-gherkin/).
Step 1 - Classify each manual step
| Tag | Pattern | Example |
|---|---|---|
| UI mechanic | Verbs like "click", "tap", "type", "press", "select from dropdown", "navigate to", "scroll" combined with a UI element. | "Click the Add to Cart button" |
| State assertion | "Verify", "check", "confirm", "ensure" combined with an observable property. | "Verify the cart count is 1" |
| Business action | A domain-level verb already (signs in, places order, cancels subscription). | "User signs in with valid credentials" |
| Setup / precondition | "Given that…", "Assuming…", "Pre-requisite:". | "Given the user is logged in" |
| Observation / data inspection | "Note the order ID for later use", "record the timestamp". | "Capture the response time" |
UI mechanics and state assertions are the two types to rewrite. Business actions are already declarative - pass them through. Setup converts to Given. Observations are typically dropped from Gherkin and lifted into step-definition implementation details.
Step 2 - Declarative-rewrite rules
The test for whether a step is too imperative: would the wording need to change if the implementation changed (e.g., the UI moved from a button to a voice command)? If yes, rewrite (https://cucumber.io/docs/bdd/better-gherkin/).
Rule R1 - Remove UI mechanics
| Imperative | Declarative |
|---|---|
| "Click the Add to Cart button" | "the customer adds a product to their cart" |
"Type user@example.com in the email field" | "the customer signs in as user@example.com" |
| "Press the Submit button" | "the customer submits the form" |
| "Select USA from the country dropdown" | "the customer chooses USA as their country" |
| "Scroll to the bottom of the page" | (drop - implementation detail; Gherkin should not require it) |
Rule R2 - Collapse multi-step UI sequences into one business action
A manual script that says "type email; type password; click Submit" becomes one Gherkin step: When the customer signs in with valid credentials. The business action is "signing in", not "clicking, typing, clicking". The mechanics live in the step definition.
Rule R3 - Replace UI properties with observable outcomes
| Imperative | Declarative |
|---|---|
| "Verify the cart count is 1" | "their cart contains one item" |
| "Confirm the Submit button is disabled" | "the form cannot be submitted" |
"Check that the URL is /dashboard" | "the customer is on the dashboard" |
| "Verify that the green checkmark appears" | "the operation is confirmed" |
Rule R4 - Choose the right keyword
| Manual step intent | Gherkin keyword |
|---|---|
| Setup state that exists before the user acts in this scenario | Given |
| The user (or system) takes an action under test | When |
| An observable consequence is asserted | Then |
| Add detail to a previous step (same keyword type) | And |
| Negate / contrast a previous step | But |
And and But inherit the type of the previous keyword - they are not interchangeable with Given/When/Then (https://cucumber.io/docs/gherkin/reference/).
Rule R5 - Preserve the project's existing vocabulary
Before emitting the rewrite, scan the project's existing Gherkin for the same business action. If "the customer signs in" is already used, do not introduce "the user logs in" - vocabulary drift fragments the step library and forces step-definition duplication. bdd-step-library-curator audits and consolidates that vocabulary.
Step 3 - Emit a side-by-side rewrite table
Output is a markdown table so a reviewer can confirm semantic equivalence:
| Manual step (input) | Gherkin step (output) | Keyword | Justification |
|---|---|---|---|
Click the Add to Cart button on the SKU-001 product page | the customer adds SKU-001 to their cart | When | R1 strips UI mechanic; business action elevated |
| Verify the cart count is 1 | their cart contains one item | Then | R3 swaps UI property for observable outcome |
| Type email; type password; click Submit | the customer signs in as user@example.com | When | R2 collapses three UI mechanics to one business action |
| Pre-requisite: User is logged in | the customer is signed in | Given | R4 chooses Given for setup |
| Note the order ID | (dropped - implementation detail) | - | Out-of-scope for Gherkin per R1 |
The surviving rows assemble into a Scenario:
Scenario: Customer adds an in-stock product to their cart
Given the customer is signed in
When the customer adds SKU-001 to their cart
Then their cart contains one itemStep 4 - Validate against project conventions
Migration-specific anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| One-for-one translation that keeps "the customer clicks the button" | Brittle to UI changes; Cucumber explicitly warns against it | Apply R1 strictly |
Multiple Whens in one scenario after collapsing | The scenario contains two business actions | Split into two scenarios |
| Inventing new vocabulary when matching steps exist | Vocabulary drift; duplicate step definitions | Cross-reference the existing library (R5) |
Dropping all Then assertions because they "look like UI checks" | The scenario becomes unverifiable | R3 rewrites assertions; doesn't drop them |
| Translating every UI mechanic, including business-relevant ones | "Selects USA as country" may be load-bearing; "scrolls to footer" is not | Preserve domain-meaningful selections |
| Gherkin that needs a comment to explain | The rewrite is wrong | Make the scenario self-explanatory |
Migration-specific limitations
Sources
Extracting acceptance criteria from raw specs - worked examples
View source (opens in new window)Extracting acceptance criteria from raw specs - worked examples
Deep reference for gherkin-from-stories ("from a raw spec / PRD" input shape). Three end-to-end examples of natural-language spec text turned into acceptance criteria; the extraction method lives in the SKILL.md section.
Example 1: simple story -> 2 scenarios
Input (Linear story):
"Title: Email update on profile page
As a logged-in user, I want to update my email address from
/profile/settings. After update, the email field shows the new value and I see a confirmation toast."
Output:
Feature: Email update on profile page
As a logged-in user
I want to update my email address from /profile/settings
So that my account contact information stays current
Background:
Given a logged-in user with email confirmed
And the user is on the /profile/settings page
Scenario: Update email to a new valid address
When the user changes the email field to "new@example.com"
And the user clicks "Save profile"
Then the email field shows "new@example.com"
And a [data-testid="profile-saved-toast"] is visible
Scenario Outline: Email update validation rejects invalid input
When the user changes the email field to "<invalid_email>"
And the user clicks "Save profile"
Then the email field shows the previous value
And a [data-testid="email-validation-error"] is visible
Examples:
| invalid_email |
| not-an-email |
| missing@ |
| @example.com |
| spaces in@email.com |Example 2: PRD with implicit preconditions
Input (PRD excerpt):
"We will allow promo codes at checkout. Codes apply 20% off the subtotal."
Output:
## Implicit-precondition flags (3)
1. **Code source.** Where do codes come from - user-entered? auto-applied? Both?
2. **Eligibility.** Are codes user-tier specific (e.g. only new users)?
Time-limited? Per-user limited?
3. **Subtotal definition.** Subtotal *before* tax / shipping, or *after*?
Without these answers, the test suite cannot be written.Never fabricate the missing pieces - return a question list to the PRD author instead.
Example 3: Scenario Outline opportunity
Input:
"Login should work for active accounts (returning 200) but reject suspended (401), unverified (403), and non-existent (404) accounts."
Output:
Feature: Login authorization
Scenario Outline: Login response per account status
Given an account with status "<status>"
When the user submits valid credentials
Then the response status is <http_status>
And the response body contains "<error_code>"
Examples:
| status | http_status | error_code |
| active | 200 | |
| suspended | 401 | ACCOUNT_SUSPENDED |
| not_verified | 403 | EMAIL_NOT_VERIFIED |
| not_found | 404 | ACCOUNT_NOT_FOUND |Related skills
bdd-step-library-curator
Keeps a BDD step-definition library DRY across a Cucumber / Behave / Reqnroll project - inventories every step definition, detects duplicates (different patterns matching the same intent), recommends canonical consolidations, reorganizes steps by domain, publishes a step-library README the team greps for "is there already a step for X?" before authoring new ones, and builds a scenario coverage map that fingerprints new Gherkin scenarios against the live suite to classify each as duplicate, partial overlap, or genuine gap before any test is authored. Use when a BDD project's step count grows past ~50, on a quarterly step-library review, when a new engineer is about to write a duplicate step, or when fresh .feature files need a covered-already check.
behave-testing
Configures Behave for Python BDD scenarios - `pip install behave`, authors `.feature` files in Gherkin, writes step implementations in `features/steps/*.py`, configures via `environment.py` for setup/teardown hooks, organizes via tags, runs via `behave`. Use for Python codebases that want Cucumber-family BDD without Cucumber-Ruby / Cucumber-JS.
cucumber-testing
Configures Cucumber for BDD scenarios - Cucumber-JVM (Java/Kotlin via JUnit 5), Cucumber-JS (Node), Cucumber-Ruby. Authors `.feature` files in Gherkin, writes step definitions in the host language, runs via the framework's runner, integrates with JUnit XML reporting. Use when the user mentions Cucumber, Gherkin, `.feature` files, or behavior-driven (BDD) tests in Java, Kotlin, JavaScript, or Ruby, as the canonical wrapper for any of the three official implementations.
living-documentation-publisher
Converts passing Cucumber JSON output into stakeholder-facing living documentation: generates HTML reports via multiple-cucumber-html-reporter (Node) or Serenity BDD aggregate (JVM), applies Gherkin tags to drive report sections, and publishes to GitHub/GitLab Pages in CI. Use when BDD scenarios are in use and the team needs an always-current, non-test-engineer-readable document showing which acceptance criteria pass.
reqnroll-testing
Configures Reqnroll (the canonical .NET BDD framework) - install via `dotnet add package Reqnroll`, author `.feature` files in Gherkin, write step bindings as `[Given/When/Then]`-decorated methods in any C# class, runs via `dotnet test`. Reqnroll is the SpecFlow successor (SpecFlow reached end-of-life 2024-12-31); covers the SpecFlow-to-Reqnroll migration path, and references/specflow-legacy.md maintains not-yet-migrated SpecFlow projects. Use for .NET projects starting BDD, migrating from SpecFlow, or maintaining legacy SpecFlow suites.