Skip to main content

BDD / Cucumber Integration

Selenium Boot integrates with Cucumber 7 out of the box. Driver lifecycle, the HTML report step timeline, screenshots on failure, metrics, and all framework features work automatically — no boilerplate in your step definitions.


Setup

Add Cucumber to your project alongside Selenium Boot:

pom.xml
<dependency>
<groupId>io.github.seleniumboot</groupId>
<artifactId>selenium-boot</artifactId>
<version>1.11.0</version>
</dependency>

<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.20.1</version>
</dependency>

<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>7.20.1</version>
</dependency>

Project structure

src/
└── test/
├── java/
│ └── com/yourcompany/
│ ├── bdd/
│ │ ├── CucumberRunner.java ← runner class
│ │ └── steps/
│ │ ├── LoginSteps.java ← extend BaseCucumberSteps
│ │ └── NavigationSteps.java
└── resources/
├── features/
│ └── login.feature
└── cucumber.properties ← for IDE single-scenario runs

Runner class

Annotate with @CucumberOptions and extend BaseCucumberTest. No other code needed:

@CucumberOptions(
features = "src/test/resources/features",
glue = {"com.yourcompany.bdd.steps", "com.seleniumboot.cucumber"},
plugin = {"pretty", "com.seleniumboot.cucumber.CucumberStepLogger"}
)
public class CucumberRunner extends BaseCucumberTest {}

"com.seleniumboot.cucumber" in glue is required — it tells Cucumber where to find CucumberHooks, which manages the driver lifecycle.

CucumberStepLogger in plugin streams Gherkin step names into the Selenium Boot HTML report step timeline.


Step definitions

Extend BaseCucumberSteps to get getDriver(), open(), $(), assertThat():

public class LoginSteps extends BaseCucumberSteps {

private LoginPage loginPage;

@Given("the user is on the login page")
public void onLoginPage() {
open(); // navigates to execution.baseUrl
loginPage = new LoginPage(getDriver());
}

@When("they login as {string} with password {string}")
public void login(String username, String password) {
loginPage.login(username, password);
}

@Then("the dashboard is visible")
public void dashboardVisible() {
assertThat(By.id("dashboard")).isVisible(); // auto-retrying assertion
}
}

BaseCucumberSteps provides:

MethodDescription
getDriver()Current thread's WebDriver
getWait()WebDriverWait using timeouts.explicit from selenium-boot.yml
open()Navigate to execution.baseUrl
open(path)Navigate to baseUrl + path
$(css)Chainable fluent locator
$(By)Chainable fluent locator
assertThat(By)Auto-retrying assertion
assertThat(Locator)Auto-retrying assertion on a locator chain
getScenario()The current Cucumber Scenario object

Feature files

Standard Gherkin — no framework-specific syntax:

src/test/resources/features/login.feature
Feature: User Login

Scenario: Valid credentials grant access
Given the user is on the login page
When they login as "admin" with password "secret"
Then the dashboard is visible

Scenario Outline: Multiple accounts can log in
Given the user is on the login page
When they login as "<username>" with password "<password>"
Then the dashboard is visible

Examples:
| username | password |
| admin | secret |
| editor | pass123 |

Each Scenario Outline example row produces a separate entry in the HTML report with its own step timeline and screenshot.


IDE single-scenario execution

When running a single scenario from the IDE (right-click → Run), the IDE uses its own runner and doesn't read @CucumberOptions. Add a cucumber.properties file so CucumberHooks is always discovered:

src/test/resources/cucumber.properties
cucumber.glue=com.yourcompany.bdd.steps,com.seleniumboot.cucumber
cucumber.plugin=pretty,com.seleniumboot.cucumber.CucumberStepLogger
cucumber.monochrome=true

Retry

Global retry

Enable retry in selenium-boot.yml — all scenarios that fail will be retried automatically:

selenium-boot.yml
retry:
enabled: true
maxAttempts: 1 # 1 retry = 2 total attempts per scenario

Per-scenario retry tag

Override the global config for individual scenarios using the @retryable or @retryable=N tag:

# Use global retry.maxAttempts from selenium-boot.yml
@retryable
Scenario: Login sometimes flakes on slow CI
Given the user is on the login page
When they submit valid credentials
Then the dashboard is visible

# Exactly 2 retries regardless of global config
@retryable=2
Scenario: Very flaky third-party widget
Given the widget is loaded
Then it should display the correct value

Tag formats:

TagBehaviour
@retryableRetry using retry.maxAttempts from config
@retryable=NRetry exactly N times (overrides config)

How it works

When a scenario fails, the entire scenario reruns from step 1 with a fresh driver. The app is in a clean state for every retry attempt.

Retried scenarios show a ↻ 1x badge in the HTML report. The final status (PASSED or FAILED after all attempts) is what appears in the report.


Run via Maven

# Run all Cucumber scenarios
mvn test -Dtest=CucumberRunner

# Run a specific feature file
mvn test -Dtest=CucumberRunner -Dcucumber.features=src/test/resources/features/login.feature

# Run scenarios tagged @smoke
mvn test -Dtest=CucumberRunner -Dcucumber.filter.tags="@smoke"

What's automatic

CucumberHooks (in the com.seleniumboot.cucumber glue package) handles everything per scenario:

EventWhat happens
Scenario startDriver created, metrics timer started, test ID registered
Step executionCucumberStepLogger logs each step name + pass/fail status into the HTML report timeline
Scenario failureScreenshot captured and embedded in both the Selenium Boot report and Cucumber's own HTML report
Scenario endDriver quit, metrics recorded, status (PASSED / FAILED / SKIPPED) written
Suite endSuiteExecutionListener.onFinish() generates the full HTML report, flakiness radar, and JSON export

Parallel execution

Set parallel and threadCount in selenium-boot.yml — the framework's ThreadLocal driver isolation makes Cucumber scenarios thread-safe:

selenium-boot.yml
execution:
parallel: methods
threadCount: 4
maxActiveSessions: 4

Each scenario runs on its own thread with its own driver instance.


Mixing Cucumber and TestNG in one suite

Selenium Boot supports both in the same Maven invocation. The HTML report combines TestNG test results and Cucumber scenario results into a single dashboard. TestExecutionListener automatically skips recording for Cucumber runner tests to avoid duplicate entries.


Attaching data to the Cucumber report

Access the current Scenario object from any step definition to attach screenshots, text, or JSON to Cucumber's own HTML report:

public class MySteps extends BaseCucumberSteps {

@Then("attach current screenshot")
public void attachScreenshot() {
String base64 = ScreenshotManager.captureAsBase64();
if (base64 != null) {
getScenario().attach(
Base64.getDecoder().decode(base64),
"image/png",
"Current state"
);
}
}
}