Cucumber Scenario Outlines: When to Use Them and When to Stop
Use Cucumber Scenario Outlines to reduce duplicated acceptance tests, but know where data-driven templates stop working.
12 Jun 2026, 13:04 UTC

Problem: One Scenario Per Input Value
A team testing a pricing engine writes a separate Gherkin scenario for every discount code and cart total. The steps are identical; only the numbers change. When the discount rule changes, someone must edit every scenario, and pull requests grow large enough to hide real logic changes.
Thesis: Scenario Outline Is for Data Variation, Not Logic Branching
Cucumber’s Scenario Outline runs one step template once per row in an Examples table. Each row substitutes values into placeholders like <total>. It keeps acceptance specs readable for non-technical reviewers while removing duplication. It is not a replacement for conditional test logic.
1. How the Outline Expands
Placeholders in angle brackets are replaced by column values from the Examples table. Each row becomes a distinct scenario execution with its own name and result. Step definitions receive the substituted values as parameters.
Scenario Outline: Discount applies to cart total
Given a cart total of <total>
When the customer applies code "<code>"
Then the discount should be <discount>
Examples:
| total | code | discount |
| 100 | SAVE10 | 10 |
| 50 | SAVE10 | 5 |
| 100 | WELCOME | 20 |
Cucumber generates three scenarios from this template. If SAVE10 behaves differently on a 50 cart, only that row fails.
2. Worked Example: Binding Data in JavaScript
Assume a Node project using @cucumber/cucumber. Run commands from the project root; no special permissions are required.
const { Given, When, Then } = require('@cucumber/cucumber');
Given('a cart total of {float}', function (total) {
this.cartTotal = total;
});
When('the customer applies code {string}', function (code) {
this.discount = applyDiscount(this.cartTotal, code);
});
Then('the discount should be {float}', function (expected) {
if (this.discount !== expected) {
throw new Error(`Expected ${expected}, got ${this.discount}`);
}
});
To verify that Cucumber binds each placeholder and expands every row, run:
npx cucumber-js --dry-run
Expected check: the output lists three generated scenarios, one per Examples row. A placeholder name typo—for example, <totals> in the template but total in the header—causes a binding error at load time in modern Cucumber JS, or may skip rows silently in older versions.
3. Trade-offs and Limits
- Diagnosis cost: A failure in row 47 of a 100-row table points to the whole row, not a single step. Large matrices make it harder to isolate which input caused the break.
- Runtime: Each row executes the full Given-When-Then flow. Suite time grows linearly with row count.
- Readability: Tables wider than four or five columns become difficult to review in pull requests.
- Wrong tool for branching: Using an outline to simulate
if/elseflows obscures business rules. Keep outlines to data variation only.
4. Practical Guidance
- Use Scenario Outline when the same steps apply to different values (roles, totals, dates, locales).
- Keep Examples headers short and exact; placeholder names must match case-for-case.
- Tag data-driven outlines (
@data-driven) and run them separately from fast feedback suites. - Move shared setup to
Backgroundso the outline stays focused on the variable data. - For more than a few dozen rows, consider external test data or a lower-level parameterized test instead of an inline Gherkin table.
After editing the feature file, run npx cucumber-js --dry-run and confirm the scenario count matches the Examples row count. If it does, the outline is bound correctly and ready for execution.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.