Data‑Driven Cucumber: Mastering Scenario Outlines, Examples, and Tagged Hooks
Keep Gherkin readable and CI fast by using Scenario Outlines with Examples tables and tagged hooks. Learn how to data‑driven tests, select subsets with tags, and run setup only when needed, with a concrete example and trade‑offs.
19 Apr 2026, 01:31 UTC

Problem
When a feature grows, you often duplicate the same scenario for different data sets or toggle parts of a test on and off in CI. Manual duplication clutters the repo, and global hooks waste resources. The challenge is to keep Gherkin readable, data‑driven, and selectively executable without scattering logic across step definitions.
Scenario Outlines Turn One Template into Many Tests
A Scenario Outline lets you write a single, business‑oriented scenario once and feed it multiple data rows via an Examples table. Each row becomes an independent test case, reported separately by the runner.
Feature: Checkout
@smoke
Scenario Outline: Successful purchase
Given I am on the product page for <product>
When I add <quantity> <product> to the cart
Then the cart total should be <total>
Examples:
| product | quantity | total |
| book | 1 | $10 |
| book | 2 | $20 |
| book | 3 | $30 |
In a single file you now have three distinct test cases. The runner will report each row’s result, making it trivial to see which data set failed.
Tagging Gives CI Granular Control
Tags can be applied at the feature, scenario, or even example row level. CI can then execute subsets with tag expressions, e.g., --tags "not @wip" to skip work‑in‑progress tests.
Feature: Checkout
@smoke @wip
Scenario Outline: Successful purchase
...
Or tag a single example:
Examples:
| product | quantity | total |
| book | 1 | $10 |
| book | 2 | $20 |
| book | 3 | $30 |
@exclude
| book | 4 | $40 |
Running cucumber --tags "not @exclude" will skip the last row while still executing the others.
Tagged Hooks Keep Setup Local
Global @Before hooks run for every scenario, even those that don’t need heavy setup. Tagged hooks run only when the scenario (or example) carries the matching tag, saving time and avoiding side‑effects.
import io.cucumber.java.Before;
public class Hooks {
@Before("@db")
public void setupDatabase() {
// expensive DB seeding
}
}
Attach @db to scenarios that need the database. The hook fires only for those, keeping unrelated tests lightweight.
Concrete Worked Example
Below is a minimal, end‑to‑end illustration using cucumber-jvm 7.x and JUnit 5. It demonstrates a Scenario Outline, row‑level tagging, and a tagged hook.
- Feature file (src/test/resources/checkout.feature)
- Step definitions (src/test/java/steps/CartSteps.java)
- Tagged hook (src/test/java/hooks/DbHook.java)
- Run command
- Expected outcome
Feature: Shopping cart
Scenario Outline: Add item and verify total
Given the user is logged in
When the user adds to the cart
Then the cart total should be
Examples:
| product | quantity | total |
| book | 1 | $10 |
| book | 2 | $20 |
@slow
| book | 5 | $50 |
import io.cucumber.java.en.*;
import static org.junit.jupiter.api.Assertions.*;
public class CartSteps {
private int cartTotal;
@Given("the user is logged in")
public void theUserIsLoggedIn() {
// stubbed login
}
@When("the user adds {int} {string} to the cart")
public void addToCart(int quantity, String product) {
cartTotal = quantity * 10; // simple pricing rule
}
@Then("the cart total should be {string}")
public void verifyTotal(String expected) {
assertEquals(Integer.parseInt(expected.replace("$", "")), cartTotal);
}
}
import io.cucumber.java.Before;
public class DbHook {
@Before("@slow")
public void heavySetup() {
System.out.println("Running heavy DB setup for slow test");
}
}
mvn test -Dcucumber.options="--tags 'not @slow'"
The first two rows run and report success. The third row is skipped because the tag expression excludes @slow. The console will show the heavySetup hook only if the @slow tag were present.
Trade‑offs and Limitations
- Readability vs. Complexity: A table with many columns can become unwieldy. If the template no longer reads like a single business scenario, split into separate scenarios.
- Tag Expression Syntax: Cucumber 7 introduced boolean tag expressions (e.g.,
~@wipvs.not @wip). Verify the runner’s documentation to avoid syntax errors. - Parallel Execution: Static fields or shared state between scenarios break when tests run in parallel. Prefer dependency injection (e.g., PicoContainer) to keep each scenario isolated.
- Cross‑Implementation Variance: Hook signatures and data‑table parsing differ between
cucumber-jvm,cucumber-js, and other bindings. Always consult the specific language guide.
Actionable Next Steps
- Audit existing features: replace duplicated scenarios with
Scenario Outlinewhere the only difference is data. - Introduce row‑level tags for long‑running or optional test data.
- Add tagged hooks for expensive setup (e.g., database seeding) to avoid unnecessary overhead.
- Update CI pipelines to use tag expressions that reflect your test strategy (e.g.,
--tags "not @wip and @smoke"). - Run a quick smoke test to confirm that each example row reports separately and that hooks fire only when expected.
By embracing Scenario Outlines, row‑level tags, and tagged hooks, you keep Gherkin clean, CI fast, and maintenance low.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.