QA How-To
Cucumber hooks and tags (2026)
Master Cucumber hooks and tags with runnable Java examples, tag expressions, hook ordering, cleanup patterns, debugging advice, and interview answers.
19 min read | 3,778 words
TL;DR
Use `Before` and `After` for scenario-scoped infrastructure, conditional hooks for capabilities such as browser or database access, and tag expressions for run selection. Keep hooks idempotent, narrow, observable, and independent of execution order whenever possible.
Key Takeaways
- Treat each hook as infrastructure, not hidden business behavior.
- Use tag expressions such as `@smoke and not @wip` for explicit selection.
- Prefer one scenario-scoped context over static mutable fields.
- Make cleanup safe after partial setup and preserve the original failure.
- Use hook order sparingly and document any dependency.
- Tag capabilities and business intent, not individuals or temporary ownership.
Cucumber hooks and tags is the practical discipline of controlling scenario lifecycle and selection without burying business behavior in test infrastructure. The reliable approach is to make lifecycle, ownership, and observable outcomes explicit, then keep framework glue smaller than the behavior it supports.
This guide moves from mental model to runnable implementation. It explains tradeoffs that appear in production suites, shows how to debug failures, and gives review criteria you can use with a team. Examples favor stable public APIs and avoid version-sensitive shortcuts.
TL;DR
| Decision | Recommended default | Reason |
|---|---|---|
| Setup for every scenario | @Before |
Consistent scenario boundary |
| Capability-specific setup | Conditional hook such as @Before("@ui") |
Avoids unnecessary resources |
| Run selection | CLI tag expression | Does not alter feature meaning |
| Cleanup | @After with null-safe guards |
Runs even after a failed step |
Use Before and After for scenario-scoped infrastructure, conditional hooks for capabilities such as browser or database access, and tag expressions for run selection. Keep hooks idempotent, narrow, observable, and independent of execution order whenever possible.
1. Cucumber Hooks and Tags: The Execution Model
A hook runs around a scenario, not around an individual Gherkin step. Model Before as construction and After as guaranteed cleanup for one scenario. Cucumber creates scenario boundaries even when scenarios execute concurrently. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
A tag is metadata inherited from a feature, rule, scenario, or examples block. Use that metadata either to select scenarios or to activate conditional infrastructure. Inheritance lets a feature-level capability apply consistently without copying tags. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
2. Choosing the Right Hook Type
Use Before and After for ordinary scenario setup and teardown. Reserve BeforeStep and AfterStep for diagnostics that truly require step granularity. Step hooks can increase report noise and runtime when they capture large artifacts repeatedly. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
An After hook is the right home for screenshots, transaction rollback, and driver shutdown. Check whether setup completed before dereferencing a resource. Null-safe cleanup handles failures that occur halfway through a Before hook. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
3. Runnable Java Hook and Tag Example
With io.cucumber:cucumber-java and the JUnit Platform engine on the test classpath, the following glue uses public Cucumber APIs. The context is created per scenario by the default object factory, so it must not be static.
package example.steps;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
public final class BrowserHooks {
private TestBrowser browser;
@Before("@ui and not @headlessOnly")
public void startBrowser() {
browser = TestBrowser.start();
}
@After("@ui and not @headlessOnly")
public void stopBrowser(Scenario scenario) {
if (browser == null) return;
if (scenario.isFailed()) {
scenario.attach(browser.screenshot(), "image/png", "failure");
}
browser.close();
}
}
Run a focused selection with the supported configuration property:
./mvnw test -Dcucumber.filter.tags="@smoke and not @wip"
The wrapper in the example represents project code with start, screenshot, and close methods, not invented Cucumber APIs. A real Selenium implementation can return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES). See Selenium framework design for driver ownership patterns.
4. Designing a Tag Vocabulary
Tags work best as a small controlled vocabulary. Separate business labels such as @checkout from capability labels such as @ui and suite labels such as @smoke. A written tag catalog prevents synonymous labels from fragmenting reports. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
Avoid encoding dates, engineer names, or ticket status as permanent taxonomy. Use issue links in reporting metadata when a temporary quarantine is required. Stable tags should answer why a scenario belongs to a meaningful group. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
5. Tag Expressions for Precise Runs
Modern Cucumber tag expressions support and, or, not, and parentheses. Quote the whole expression in a shell so the command interpreter does not split it. @smoke and not @slow is easier to audit than several overlapping runner classes. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
Test expressions against a dry run or a known feature set before changing CI. Print the selected scenario count and names in pipeline output. An unexpectedly empty selection should fail fast rather than report a misleading green build. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
6. Conditional Hooks Without Hidden Behavior
A conditional hook should provision a technical capability named by its tag. Let @api create an API client and @ui create a browser session. Do not use a hook tagged @premium to silently create a premium customer because that is business state. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
Keep preconditions visible in Given steps when a reader needs them to understand the outcome. Move only repetitive transport and resource management behind hooks. This boundary preserves living documentation while reducing mechanical noise. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
7. Ordering, Failure, and Cleanup
Cucumber supports ordered hooks, but order creates coupling. Use order only when independent hooks cannot express a clear resource dependency. Document whether lower ordered Before hooks run earlier and verify behavior in your pinned release. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
Cleanup must tolerate partially initialized state and multiple calls. Close resources in reverse ownership order and add suppressed cleanup errors to diagnostics. Never replace the scenario failure with an uninformative driver quit exception. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
8. Parallel Execution and Isolation
Parallel runs reveal every static cache and shared mutable singleton. Store drivers, clients, and scenario data in scenario-scoped dependency injection objects. A scenario should produce the same result alone, shuffled, or on another worker. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
Tags are not locks and must not be used to coordinate shared accounts. Allocate unique test data or use a concurrency-safe pool with explicit leases. Release leases in cleanup even when assertions fail. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
9. Reporting and Debugging Hooks
Attach concise text, JSON, or screenshots through the Scenario object only when useful. Name artifacts with the scenario and a collision-safe identifier. Large unconditional attachments slow reports and hide the evidence engineers need. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
Log hook start, resource identity, and cleanup status at debug level. Avoid secrets, authorization headers, and complete personal records. Useful diagnostics explain lifecycle without turning reports into a data leak. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
10. CI Suite Design With Tags
Define a few pipeline contracts such as pull request smoke, nightly regression, and quarantined review. Keep their tag expressions in version control beside the test configuration. Review expression changes like code because they change what quality signal the pipeline provides. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
Measure unselected and quarantined scenarios as visible debt. A passing job that silently excludes growing areas is not a reliable release gate. Publish counts by tag so scope changes are intentional. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
11. Cucumber hooks and tags Review Checklist
Inventory hooks by resource, condition, order, and side effect before editing them. Move business data creation into readable Given steps and consolidate duplicate technical setup. Run scenarios in random order after each small change. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
Add contract tests for context factories and cleanup helpers where failure would be expensive. Keep one end-to-end feature as a lifecycle canary. The related Cucumber step definitions in Java guide helps keep glue equally focused. This matters in a real suite because a scenario should explain one business outcome while the automation supplies only the technical detail needed to prove it. When the implementation is explicit, a failed build tells the team what changed instead of forcing an engineer to reverse engineer hidden framework behavior.
A useful review question is: "Could another engineer predict the scope, timing, and failure mode from this code?" If the answer is no, simplify the boundary and give the shared object or helper a name that describes its responsibility. Keep assertions close to the observable outcome, log identifiers that help reproduce a failure, and remove cleanup logic that can mask the original exception. These habits make Cucumber hooks and tags maintainable under parallel execution and routine product change.
Interview Questions and Answers
Q: undefined
undefined
Q: undefined
undefined
Q: undefined
undefined
Q: undefined
undefined
Q: undefined
undefined
Q: undefined
undefined
Q: undefined
undefined
Common Mistakes
Putting business setup in hooks: Readers cannot see why the outcome is valid. Put meaningful preconditions in Given steps.
Using static drivers: Parallel scenarios overwrite one another and cleanup becomes nondeterministic.
Creating dozens of synonyms: Tags such as
@quick,@fast, and@smokemake selection ambiguous.Trusting order as architecture: Ordered hooks hide dependencies that should be represented by one owner.
Attaching everything: Unbounded screenshots and payloads make reports slow and hard to inspect.
Ignoring empty selections: A green job with zero scenarios provides no evidence.
Conclusion
Effective Cucumber hooks and tags create an obvious lifecycle: selection is intentional, technical resources are scenario scoped, and cleanup remains dependable after partial failure. Start with one representative feature, run it alone and in parallel, and review the resulting report with both an automation engineer and a product stakeholder. That small exercise exposes unclear ownership early and gives the team a repeatable standard for the rest of the suite.
Related Guides
- Java for Testers: Cucumber hooks and tags (2026)
- Cucumber data tables and scenario outline (2026)
- How to Build a BDD framework with Cucumber and Playwright (2026)
- AI test data generation with Faker and LLMs (2026)
- API error handling and negative testing: A Practical Guide (2026)
- Appium gestures and swipe: A Practical Guide (2026)