Dry Run Concept in Cucumber JVM
1. Introduction to Dry Run in Cucumber
Dry Run in Cucumber JVM is a special execution mode used to validate feature files and step definition mappings without actually executing the automation code inside the step definition methods. It is a framework validation mode, not a functional test execution mode. When dry run is enabled, Cucumber reads the feature files, parses the Gherkin, searches the configured glue packages, checks whether each step has a matching step definition, reports missing or ambiguous mappings, and then stops without opening a browser, calling an API, touching a database, or running business validation logic.
This concept is extremely useful during BDD framework development because feature files are often written before all Java step definitions are implemented. A tester or business analyst may create a new scenario that describes the expected behavior. An automation engineer then needs to know which steps are already mapped and which steps still need Java methods. Running the full automation suite just to discover missing steps can waste time, especially if the suite launches browsers, prepares environments, creates test data, or executes slow end-to-end flows. Dry Run gives a fast answer: are the feature files and step definitions wired correctly?
In practical terms, Dry Run is like asking Cucumber, "Can you understand and map these scenarios?" It does not ask, "Does the application work?" That distinction matters. A successful dry run does not prove that login works, payment works, Selenium locators are stable, API responses are correct, or assertions are valid. It only proves that Cucumber can parse the scenarios and find matching step definition methods. Used correctly, Dry Run saves time and catches mapping issues early. Used incorrectly, it can create a false sense that tests passed when no real test logic actually executed.
2. What Is Dry Run?
Validation Without Automation Execution
A Dry Run is a Cucumber execution mode where Cucumber validates scenario structure and step mapping without running the code inside the step definitions. If a step definition method normally launches a browser, enters data, calls a service, or performs assertions, none of that logic runs during dry run. Cucumber only checks whether the method exists and whether the annotation pattern can match the Gherkin step.
In normal execution, this step:
When the user logs in
may execute this Java method:
@When("the user logs in")
public void userLogsIn() {
loginPage.login();
}
During dry run, Cucumber verifies that the annotation exists and matches the step text. It does not call loginPage.login(). That means no browser interaction happens and no assertion is verified. The step is considered mapped, but the behavior is not tested.
This makes Dry Run ideal for checking framework completeness. It is especially valuable when adding new features, reorganizing glue packages, refactoring step definitions, or reviewing whether a set of feature files is ready for real automation execution.
3. Why Dry Run Is Needed
Dry Run is needed because BDD projects separate readable scenarios from executable code. Feature files may be created by testers, automation engineers, developers, product owners, or business analysts. Step definitions are usually implemented by automation engineers or developers. These two layers do not always evolve at exactly the same speed. Dry Run helps identify gaps between them quickly.
Without dry run, a team might run a full suite and wait for browser setup, environment loading, test data creation, and scenario execution only to discover that the first missing step stops the run. The team then implements that one step, runs the suite again, and discovers the next missing step. This loop is slow and frustrating. Dry Run shortens the feedback loop by checking mapping across scenarios before automation code executes.
It also helps detect glue configuration mistakes. A project may contain the correct step definition classes, but if the runner points to the wrong glue package, Cucumber will report steps as undefined. Dry Run catches this quickly because it focuses on discovery and mapping. It can also reveal ambiguous step definitions before real execution begins.
4. How Dry Run Works Internally
Parsing, Discovery, and Mapping
When dry run is enabled, Cucumber follows many of the same early steps as normal execution. It reads the runner configuration, locates feature files, parses Gherkin syntax, loads glue packages, registers step definition patterns, and attempts to match each Gherkin step to a Java annotation. The difference is what happens after mapping. In normal execution, the matched Java method is invoked. In dry run, Cucumber stops after validation and does not invoke the method body.
Normal execution:
Feature file
|
v
Find matching step definition
|
v
Execute Java method
|
v
Browser / API / database / assertions
|
v
Generate report
Dry run execution:
Feature file
|
v
Find matching step definition
|
v
Validate mapping
|
v
Report mapping problems
|
v
Stop before automation logic
This flow explains why no browser opens during dry run. Even if the step definition contains Selenium code, Cucumber does not execute that method body. It only verifies that the method is available as a mapping target.
5. Configuring Dry Run
In many Cucumber JVM projects, Dry Run is configured through the dryRun option in @CucumberOptions. A basic runner may look like this:
@RunWith(Cucumber.class)
@CucumberOptions(
features = "src/test/resources/features",
glue = "stepdefinitions",
dryRun = true
)
public class TestRunner {
}
The important setting is dryRun = true. This tells Cucumber to validate feature files and step definition mappings only. When it is time to run actual automation, the value should be changed to false:
dryRun = false
Some modern projects configure Cucumber using JUnit Platform properties, Maven profiles, Gradle settings, or command-line options. The exact configuration style can differ, but the meaning is the same. A dry run validates the Cucumber layer without executing the automation layer.
6. Dry Run vs Normal Execution
Same Mapping, Different Execution Behavior
Normal execution and dry run both read feature files and search for step definitions. The difference is that normal execution runs the step method body, while dry run does not. This distinction affects browser behavior, API calls, database operations, assertions, reports, and execution time.
| Aspect | Normal Execution | Dry Run Execution |
|---|---|---|
| Step mapping | Checked | Checked |
| Step method body | Executed | Not executed |
| Browser launch | Possible | No |
| API calls | Possible | No |
| Assertions | Executed | Not executed |
| Main purpose | Validate application behavior | Validate framework wiring |
This table shows why dry run should never replace real testing. It is a quick validation step before real execution. It answers whether the Cucumber framework is ready to execute, not whether the system under test behaves correctly.
7. What Happens During Dry Run?
Suppose a feature file contains this scenario:
Scenario: Login
Given the user is on the login page
When the user enters valid credentials
Then the dashboard should be displayed
During dry run, Cucumber reads the scenario and checks each step. For the Given step, it searches glue packages for a matching @Given annotation. For the When step, it searches for a matching @When annotation. For the Then step, it searches for a matching @Then annotation. If all steps are found, dry run completes successfully from a mapping perspective.
Read feature file
|
v
Search glue packages
|
v
Find matching step definitions
|
v
All steps mapped?
|
+-- Yes -> Finish validation
|
+-- No -> Report undefined steps
If a step is missing, Cucumber reports it as undefined and often prints a suggested step definition snippet. If multiple definitions match, Cucumber reports ambiguity. In both cases, no automation logic is executed.
8. Example with an Undefined Step
Imagine the feature file contains:
Given the user is logged in
If there is no matching step definition, dry run output may include a generated snippet:
@Given("the user is logged in")
public void the_user_is_logged_in() {
throw new io.cucumber.java.PendingException();
}
The important point is that the browser is never opened. Cucumber does not attempt to log in. It simply reports that the step is not mapped and provides a template for implementation. The developer can copy the snippet into the proper step definition class, remove PendingException, and add real implementation later.
This is one of the strongest uses of dry run. It turns missing mappings into a clear implementation checklist without waiting for slow automation setup.
9. What Dry Run Checks
Framework-Level Validation
Dry Run checks the structure and wiring of the Cucumber framework. It validates whether feature files can be parsed, whether Gherkin syntax is acceptable, whether glue packages are configured well enough for discovery, and whether each scenario step maps to a step definition. It can also expose ambiguous step definitions when multiple methods match the same phrase.
- Feature file syntax and Gherkin parsing.
- Step definition mapping.
- Glue package configuration.
- Undefined steps.
- Ambiguous step definitions.
- Cucumber Expression and regular expression matching.
- Basic readiness of feature files for automation execution.
These checks are valuable before committing changes. If a developer adds ten scenarios and dry run shows five missing steps, the developer can implement or reuse mappings before opening a pull request. If dry run shows ambiguity, the developer can fix duplicate step definitions before they affect the team.
10. What Dry Run Does Not Check
Dry Run does not validate application behavior. It does not click buttons, load pages, submit forms, call APIs, read databases, validate UI states, or assert business rules. It also does not verify that Selenium locators are correct, that waits are stable, that test data exists, or that the environment is available.
- It does not verify Selenium automation logic.
- It does not execute API requests.
- It does not perform database operations.
- It does not validate assertions.
- It does not prove the application works.
- It does not catch runtime failures inside step methods.
This limitation is not a weakness. It is the purpose of dry run. Dry Run is intentionally narrow so it can provide fast feedback on the Cucumber layer. Real test execution is still required to validate product behavior.
11. Dry Run in the Development Workflow
Fast Feedback Before Full Execution
A practical BDD workflow often starts with writing or updating feature files. After scenarios are reviewed for business clarity, the automation engineer runs dry run. Dry Run shows which steps are already mapped, which steps are missing, and whether any step definitions conflict. The engineer then implements missing mappings, cleans up duplicates, and runs dry run again until the Cucumber wiring is clean.
Write feature file
|
v
Run dry run
|
v
Fix undefined or ambiguous steps
|
v
Run dry run again
|
v
Run real automation
|
v
Execute smoke or regression suite
This workflow reduces debugging time because mapping problems are handled before browser or API execution begins. Once dry run passes, any failures in normal execution are more likely to come from actual automation logic, environment behavior, application bugs, test data, assertions, or synchronization issues.
12. Dry Run vs Undefined Step Generation
Dry Run and Undefined Step Generation are closely related, but they are not identical. Dry Run is the validation mode. Undefined Step Generation is the helpful output Cucumber provides when it finds missing mappings. Dry Run can trigger undefined step generation because it checks all steps without running automation.
| Aspect | Dry Run | Undefined Step Generation |
|---|---|---|
| Purpose | Validate mapping without execution | Suggest missing step templates |
| When used | Before real automation execution | When a step has no match |
| Output | Mapping status and errors | Step definition snippets |
| Automation logic | Not executed | Not executed by itself |
If dry run reports an undefined step, the generated snippet can be used to create the missing step definition. After implementation, dry run should be run again to confirm the step now maps correctly.
13. Dry Run vs Strict Mode
Older Cucumber discussions often mention strict mode. Historically, strict mode influenced how undefined and pending steps affected the build result. In modern Cucumber JVM versions, especially Cucumber v7 and later, the old strict option has been removed because undefined and pending steps fail execution by default. This makes a separate strict setting unnecessary in many modern projects.
| Aspect | Dry Run | Strict Mode |
|---|---|---|
| Purpose | Validate mapping before execution | Fail build for undefined or pending steps |
| Execution | Stops before step bodies run | Normal execution behavior |
| Browser | Does not open | May open during normal execution |
| Modern status | Still commonly used | Old option removed in newer versions |
The interview-safe explanation is that dry run validates mappings without executing automation, while strict mode was about failing the run for undefined or pending steps. In current Cucumber JVM projects, undefined and pending steps are generally treated as failures by default.
14. Real-Time Example: Search Product Scenario
Consider this runner:
@RunWith(Cucumber.class)
@CucumberOptions(
features = "src/test/resources/features",
glue = "stepdefinitions",
dryRun = true
)
public class TestRunner {
}
Now consider this feature:
Scenario: Search product
Given the application is open
When the user searches for "Laptop"
Then matching products should be displayed
If all step definitions exist, dry run completes without launching the application. If the search step is missing, Cucumber reports an undefined step and suggests a method similar to this:
@When("the user searches for {string}")
public void the_user_searches_for(String product) {
throw new io.cucumber.java.PendingException();
}
The developer can then add this method to SearchSteps.java and implement it properly. A clean implementation might delegate to a page object:
@When("the user searches for {string}")
public void userSearchesFor(String product) {
searchPage.searchFor(product);
}
After mapping is complete, dryRun should be set to false for real execution so the browser actually opens and the search behavior is verified.
15. Advantages of Dry Run
Speed and Early Detection
The biggest advantage of dry run is fast feedback. It can identify undefined and ambiguous steps without waiting for browser startup, login flows, test data setup, remote grids, or external systems. This makes it valuable during active framework development and scenario authoring.
Dry Run also improves productivity because it creates a clear list of missing mappings. Instead of discovering one missing step at a time during full execution, the team can see mapping problems earlier. It also helps validate that glue configuration is correct after package refactoring.
- Quickly identifies missing step definitions.
- Saves execution time during development.
- Detects incorrect glue configuration early.
- Finds Gherkin syntax problems before full runs.
- Helps developers implement feature files faster.
- Useful before committing new scenarios.
16. Limitations of Dry Run
Dry Run has clear limitations. It cannot tell whether a step definition method works because the method body is not executed. A step may map successfully but still fail during normal execution because of a bad locator, missing wait, incorrect assertion, broken API endpoint, wrong database query, or unavailable environment.
For example, this method may map during dry run:
@Then("the dashboard should be displayed")
public void dashboardShouldBeDisplayed() {
assertTrue(dashboardPage.isDisplayed());
}
Dry run only confirms that the annotation matches the Gherkin step. It does not call dashboardPage.isDisplayed(). If that page object method has a broken locator, dry run will not catch it. Only normal execution can reveal that runtime failure.
Therefore, dry run should be used before real execution, not instead of real execution. It validates framework wiring. It does not validate application quality.
17. Common Mistake: Forgetting to Disable Dry Run
Why Nothing Seems to Execute
One of the most common mistakes is leaving dryRun = true and then wondering why Selenium does not open a browser. This happens often when developers use dry run during step definition development and forget to switch it back before normal test execution.
@CucumberOptions(
dryRun = true
)
With this setting, Cucumber validates mapping and stops. The browser does not open because step bodies are not called. Before running actual automation, change it back:
@CucumberOptions(
dryRun = false
)
A good practice is to avoid hard-coding dryRun = true in a shared regression runner. Instead, use a separate runner, Maven profile, Gradle task, or configuration property for dry-run validation. This reduces the risk of accidentally disabling real execution for the whole team.
18. Common Mistake: Assuming Dry Run Means Tests Passed
A successful dry run does not mean tests passed. It means Cucumber was able to parse the feature files and map the steps. No business behavior was verified. No application functionality was tested. No assertions ran. This misunderstanding can be serious if dry run output is treated as a test result.
For example, a dry run may pass even if the login page is down, the browser driver is broken, the API endpoint returns 500, or the database has no test data. Because dry run never executes those layers, it cannot detect those problems.
The correct interpretation is: "The Cucumber framework is wired well enough to attempt execution." The next step is to run the actual automation suite with dryRun disabled.
19. Common Mistake: Using Dry Run as CI Regression
Dry Run can be useful in CI as a lightweight validation check, but it should not replace smoke, regression, or end-to-end execution. A CI pipeline that only runs dry run is not testing the application. It is only checking Cucumber mappings.
A better pipeline might include dry run as an early stage for fast feedback, followed by actual test execution:
Stage 1: Compile test code
Stage 2: Run Cucumber dry run
Stage 3: Run smoke automation
Stage 4: Run regression automation
Stage 5: Publish reports
This structure uses dry run for what it does best while still preserving real testing. If dry run fails, the pipeline can stop early because the framework is not ready to execute. If dry run passes, the suite can continue to real validation.
20. Best Practices for Dry Run
Use It as a Framework Readiness Check
Use dry run while developing new feature files or refactoring step definitions. Run it after adding scenarios to catch undefined steps quickly. Run it after changing glue package names to confirm Cucumber can still discover step definitions. Run it before full execution when you want fast confidence that the BDD layer is wired correctly.
Keep dry run separate from normal execution. Use clear runner names or build profiles so developers know whether they are validating mappings or executing tests. Do not leave dryRun = true in a default runner used for regression. Fix undefined and ambiguous steps before committing code whenever possible.
- Use dryRun = true during scenario and step development.
- Switch to dryRun = false before real automation execution.
- Run dry run after adding or changing feature files.
- Fix undefined and ambiguous steps early.
- Do not treat dry run as a functional test result.
- Keep dry-run configuration separate from regression configuration.
21. Dry Run and Glue Package Refactoring
Dry Run is very useful when refactoring package structure. In Cucumber JVM projects, step definitions and hooks are discovered through glue packages. If classes are moved from one package to another, the runner configuration must be updated. Otherwise, Cucumber may report many undefined steps even though the Java methods still exist in the repository.
After refactoring, running dry run gives quick feedback. If many steps become undefined, check the glue configuration before rewriting step definitions. The likely problem may be that Cucumber is searching an old package name. Dry run makes this visible without running Selenium or other automation code.
This is especially useful in large frameworks where moving classes can affect hundreds of scenarios. A dry run can detect the impact quickly and help confirm that package organization is still correct.
22. Dry Run and Ambiguous Step Cleanup
Dry Run can also expose ambiguous steps. Because Cucumber still performs step matching, it can detect when more than one step definition matches the same Gherkin step. This is useful during cleanup of duplicate step definitions or broad regular expressions.
If dry run reports ambiguity, do not add another step definition. The fix is to remove duplicates, narrow expressions, or standardize scenario wording. A dry run that passes after cleanup gives confidence that each step maps to exactly one method before full execution begins.
Ambiguity cleanup improves long-term framework quality. It makes reports clearer, step definitions easier to maintain, and feature files more consistent.
23. Dry Run in Team Collaboration
Helping Testers and Developers Stay Aligned
Dry Run supports collaboration because it gives immediate feedback on whether feature files and automation code are aligned. A tester may write new scenarios, run dry run, and share the undefined step list with an automation engineer. A developer may refactor step packages and use dry run to confirm that no mapping was lost. A reviewer may ask for dry run output before approving a pull request that adds many feature scenarios.
This makes dry run a communication tool as well as a technical option. It shows the current contract between business-readable Gherkin and executable Java. If the contract is incomplete, the team can fix it before spending time on full browser execution.
For large teams, dry run can also help enforce shared vocabulary. If a new phrase generates an undefined step but an existing phrase already describes the same behavior, the team can update the scenario wording instead of creating duplicate step definitions.
24. Troubleshooting Dry Run Problems
If dry run reports undefined steps unexpectedly, check the annotation text first. Make sure the wording in the feature file matches the annotation expression. Then check the glue package. Confirm that the step definition class is under the package Cucumber is scanning. Also confirm that the correct runner or build profile is being used.
If dry run reports ambiguous steps, read the listed methods and remove the conflict. Search for duplicate annotation text and broad expressions. If dry run appears to do nothing, verify that the runner is actually executing the feature files and that tag filters are not excluding every scenario.
If developers expect browsers to open during dry run, the fix is educational: dry run is not supposed to execute step bodies. Set dryRun to false or use the normal execution runner.
25. Safe Dry Run Configuration Strategy
Separate Validation from Real Execution
A safe strategy is to keep dry-run execution clearly separate from normal test execution. In small projects, this may mean using a dedicated runner class named DryRunRunner. In larger projects, it may mean using Maven profiles, Gradle tasks, or environment-based configuration. The goal is to make it obvious whether the current command is only validating Cucumber mappings or actually executing automation.
This separation prevents one of the most frustrating mistakes in Cucumber projects: accidentally running regression with dryRun enabled. When dry run is hidden inside the default runner, a developer may see a quick green run and assume the test suite passed, even though no browser, API, database, or assertion code was executed. Clear naming and separate commands reduce that risk.
For local development, dry run can be part of the routine after writing or editing feature files. For CI, it can be an early validation stage that fails fast when mappings are broken. It should be followed by real smoke or regression execution so the application behavior is actually verified. This keeps dry run useful without allowing it to replace testing.
26. Interview-Ready Summary
Short Explanation for Interviews
Dry Run in Cucumber is an execution mode that validates feature files and step definition mappings without executing automation code. It is enabled using dryRun = true in @CucumberOptions or an equivalent configuration. During dry run, Cucumber parses Gherkin, searches glue packages, checks step mappings, reports undefined steps, and detects ambiguous mappings, but it does not launch browsers, call APIs, access databases, or run assertions.
A strong interview answer should mention that dry run is useful during framework development because it quickly identifies missing step definitions and glue configuration issues. It should also clarify that dry run does not prove the application works. It validates the Cucumber framework, not application functionality.
- Dry Run validates feature files and step mappings.
- It does not execute step definition method bodies.
- It helps find undefined and ambiguous steps early.
- It saves time during BDD framework development.
- It must not replace actual automation execution.
27. Golden Rule
The golden rule is simple: Dry Run validates your Cucumber framework, not your application. It ensures every Gherkin step has exactly one matching step definition before real automation begins. It is a readiness check for the BDD layer, not proof that the system under test behaves correctly.
Use dry run to catch missing mappings, wrong glue packages, syntax issues, and ambiguous definitions early. Then switch to normal execution to validate real behavior through Selenium, APIs, databases, assertions, and reports. When used in the right place, Dry Run makes Cucumber JVM development faster, cleaner, and easier to maintain.