Cucumber Background as a Design Boundary: Shared Setup and Failure Containment
Treat Cucumber Background as an architectural boundary: what belongs in it, how it fails, why silent skips can hide setup defects in CI, and when to move setup elsewhere.
19 Jul 2026, 20:32 UTC

Most Cucumber suites grow a Background block by accident: someone gets tired of repeating the same three Given steps in every scenario, hoists them to the top of the feature file, and moves on. That works until the Background becomes a junk drawer of unrelated setup, a single point of failure that skips entire feature files, or a hidden coupling that makes scenarios impossible to read in isolation. This note treats Background as an architectural boundary — a deliberate decision about what state is shared, who owns it, and what happens when it breaks.
Requirements the design must satisfy
A shared-setup mechanism in a BDD suite has to do four things well:
- Eliminate duplication without hiding intent. A reader should understand a scenario from the feature file alone, without opening step definitions to discover what state exists.
- Fail fast and fail visibly. If shared preconditions cannot be established, no scenario should run against undefined state, and the failure should be attributable to setup, not to the behavior under test.
- Stay cheap. Background runs before every scenario in the file, so anything in it is paid N times per feature.
- Compose with selective execution. Teams running tagged subsets in CI need shared setup to follow the same filtering rules as scenarios.
The smallest suitable design
Background in Cucumber executes its steps before each scenario in the same feature file, in order, before the scenario's own steps. It supports the full Given/When/Then syntax, including Data Tables and Doc Strings, so parameterized shared state is possible without inventing a new mechanism:
Feature: Order cancellation
Background:
Given a customer account exists with email "[contact removed]"
And the customer has a confirmed order containing:
| sku | quantity |
| BOOK-101 | 1 |
Scenario: Cancel before dispatch
When the customer cancels the order
Then the order status is "cancelled"
Scenario: Cancel after dispatch
Given the order has been dispatched
When the customer cancels the order
Then the cancellation is rejected with reason "already dispatched"Both scenarios share the account and order fixture; the second adds its own precondition (dispatch) on top. The Background holds only what is true for every scenario in the file — that is the whole design rule.
Trust and data boundaries
Background steps run inside the same scenario lifecycle as the scenario's own steps, so they share the same world/instance state and the same hooks. Two boundary rules follow:
- Visibility. Step definitions referenced from a Background must be on the glue path for that feature. Ambiguous or duplicate step definitions surface as errors at runtime — and in a Background they take down every scenario in the file, not just one.
- Isolation. Because Background re-runs per scenario, it must not depend on state left behind by a previous scenario. If your Background mutates shared external state (a database row with a fixed email, say), parallel or repeated runs will collide. Prefer unique-per-run data built inside the Background itself.
Failure behavior and operational checks
If any Background step fails, Cucumber marks the scenario as failed (or skipped, depending on version and formatter) and does not execute the scenario body. That is the desired containment: no test logic runs against undefined state. The operational risk is that a broken fixture can quietly skip an entire feature file, and a CI pipeline that only counts red scenarios may not notice a suite that shrank overnight.
Practical checks:
- Verify the boundary locally. Introduce a deliberate failure in a Background step, run the single feature, and confirm the scenario body never executes and the report attributes the problem to the Background step.
- Watch skip counts in CI. Run with a JSON formatter (for example
--format json:build/cucumber.jsonon the JVM, or the equivalent for your flavor) and fail the build if the skipped-scenario count rises unexpectedly. Treat a spike in skips as a setup defect, not as flaky tests. - Check glue health. Run with strict mode enabled so undefined or ambiguous steps in a Background fail loudly during local development instead of surfacing as mass skips in CI.
Commands are run from the project root with the same permissions as your normal test task; the JSON report path must be writable by the CI agent.
Failure modes and when to change the design
The common failure modes are all forms of boundary erosion:
- Junk-drawer Background. Ten lines of setup where each scenario only needs part of it. Readers can no longer tell which state matters. Move scenario-specific steps back into the scenarios.
- Expensive shared setup. Background runs N times per feature. Heavy operations (migrations, external service provisioning) belong in a
BeforeAll-style hook or test fixture, not in Background. - Hidden coupling. Background steps that mutate shared external state cause collisions under parallel execution. Generate unique data per run inside the Background.
- Silent mass skips. One broken Background step skips the whole file; without skip-count monitoring in CI this masks setup defects.
Conditions that should push you off Background entirely: setup that differs per scenario (use tagged hooks or profiles for selective setup instead), setup that must run once per suite (use a global hook), or preconditions so central to the behavior being described that hiding them in a Background makes the scenario unreadable. Background is the right tool exactly when the setup is identical, cheap, and uninteresting to the behavior under test.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.