Cucumber is a testing framework that works on the Behavior-Driven Development (BDD) approach. It allows test cases to be written in a human-readable language, so developers, testers, and business teams can all understand what is being tested.
When combined with Selenium, Cucumber provides a way to describe browser-based automation in plain text while keeping the technical implementation separate. This makes test automation both collaborative and maintainable.
This article explains the essentials of the Cucumber framework in Selenium, its structure, advanced techniques, and how it can be effectively used in real projects.
Selenium enables browser automation, but its scripts are written in code that is often inaccessible to non-technical stakeholders. Cucumber adds value by introducing a Behavior-Driven Development (BDD) layer, making tests both descriptive and executable.
Here are the key reasons why it is essential:
Cucumber provides a structured way to connect plain-text scenarios with Selenium automation. Its key features define how tests are written, executed, and maintained, which makes the framework practical for real-world projects.
Here are the most important ones to understand:
Before writing tests, the environment must be configured to support both Selenium automation and Cucumber’s BDD structure. Preparation involves setting up the project, adding the required dependencies, and organizing files so feature files, step definitions, and runner classes can work together.
Here is how to prepare the environment step by step:
1. Install Java Development Kit (JDK)
Download and install the latest stable JDK version. Configure the JAVA_HOME environment variable and verify the installation by running java -version in the terminal.
2. Set up an IDE
Install an Integrated Development Environment such as IntelliJ IDEA, Eclipse, or Visual Studio Code. These IDEs provide plugins for Cucumber, allowing navigation from feature steps to step definitions.
3. Create a new Maven or Gradle project
Start a new project in your IDE. Use Maven (pom.xml) or Gradle (build.gradle) for managing dependencies. This avoids manual downloads and ensures libraries stay updated.
4. Add Selenium and Cucumber dependencies
In Maven, update the pom.xml with the required libraries:
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.20.0</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.14.0</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>7.14.0</version>
</dependency>
This links Selenium with Cucumber and enables execution with JUnit.
5. Download browser drivers
Install the driver for the browser you plan to test, such as ChromeDriver or GeckoDriver. Place the driver path in the system environment or configure it directly in the code.
6. Set up project structure
Create dedicated folders for:
This organization keeps files clean and easy to navigate.
7. Verify setup with a sample feature file
Write a small feature such as opening a browser and verifying the page title. Link it to a step definition and execute it with the runner. A successful run confirms the environment is ready.
Writing effective tests in Cucumber requires a balance between readability, maintainability, and reliability. Tests should clearly reflect business behavior while being structured for efficient automation. Poorly written scenarios can become hard to maintain, difficult to understand, and prone to breaking when the application changes.
Below are key practices for writing effective Cucumber tests:
Once the basics of Cucumber testing are in place, advanced techniques help make tests more maintainable, reusable, and scalable. Features like parameterization, hooks, tags, and data-driven testing reduce duplication, control setup and teardown, and allow targeted execution of scenarios.
The following examples illustrate how these techniques can be applied in Selenium projects.
Parameterization allows the same scenario to be executed with multiple sets of data. Instead of writing separate scenarios for each input, a single Scenario Outline can iterate through different values, reducing duplication and improving coverage.
Hooks allow setup and teardown activities to run automatically before or after scenarios. This ensures that each scenario starts with the required environment and ends with proper cleanup.
Example in Java:
@Before
public void setUp() {
driver = new ChromeDriver();
driver.manage().window().maximize();
}
@After
public void tearDown() {
driver.quit();
}
The @Before hook initializes the browser and maximizes the window before each scenario, while @After closes the browser and releases resources after the scenario completes.
Tags provide a way to group scenarios and execute only specific subsets of tests. This is useful for running smoke tests, regression suites, or functional modules independently.
Example feature file:
@SmokeTest
Scenario: Verify login functionality
Given User is on the login page
When User enters valid credentials
Then User should be redirected to the dashboard
Test runner configuration:
@CucumberOptions(
features = "src/test/resources/features",
glue = "stepdefinitions",
tags = "@SmokeTest"
)
public class TestRunner {}
Only scenarios tagged with @SmokeTest will run. This allows efficient testing without modifying the feature files themselves.
Data-driven testing allows tests to read inputs from external sources like CSV, Excel, or JSON. This approach separates test data from test logic, making tests easier to maintain and flexible when input values change.
Example step definition reading a CSV file:
@When("User logs in with data from CSV")
public void loginWithCsvData() throws IOException {
List<String[]> testData = CSVReader.read("testdata.csv");
for(String[] row : testData) {
driver.findElement(By.id("username")).sendKeys(row[0]);
driver.findElement(By.id("password")).sendKeys(row[1]);
driver.findElement(By.id("loginButton")).click();
driver.navigate().back();
}
}
Here, multiple login scenarios are executed dynamically based on the CSV content, reducing the need to hardcode credentials in the feature file.
Complex or repeated Selenium actions can be moved to reusable methods. This allows step definitions to remain concise and reduces duplication across scenarios.
Example reusable method:
public void login(String username, String password) {
driver.findElement(By.id("username")).sendKeys(username);
driver.findElement(By.id("password")).sendKeys(password);
driver.findElement(By.id("loginButton")).click();
}
Step definitions can call this method in multiple scenarios, making updates faster when UI locators change.
Running isolated tests is only part of automation. In real projects, Cucumber tests must integrate with Selenium in a way that supports team collaboration, continuous integration, and consistent reporting. Proper integration ensures tests are reusable, maintainable, and can be executed across environments and browsers.
A well-defined project structure separates feature files, step definitions, and test runners. This improves readability and maintainability when the project scales.
Keeping feature files, step definitions, and runners in separate folders prevents confusion and allows parallel development by multiple testers.
Cucumber test runners define which feature files and step definitions to execute, as well as reporting formats and tags. This enables selective execution of tests in CI/CD pipelines.
Example runner configuration:
@RunWith(Cucumber.class)
@CucumberOptions(
features = "src/test/resources/features",
glue = "stepdefinitions",
plugin = {"pretty", "html:target/cucumber-report.html"},
tags = "@Regression"
)
public class TestRunner {}
This configuration executes all scenarios tagged with @Regression and generates an HTML report for easy analysis.
In real projects, tests must run across multiple browsers to ensure compatibility. Selenium WebDriver can be configured to run on different browsers using a parameter or a configuration file.
Example cross-browser setup:
public WebDriver initializeDriver(String browser) {
if(browser.equalsIgnoreCase("chrome")) {
return new ChromeDriver();
} else if(browser.equalsIgnoreCase("firefox")) {
return new FirefoxDriver();
} else {
throw new IllegalArgumentException("Unsupported browser: " + browser);
}
}
This allows the same Cucumber tests to be executed on Chrome, Firefox, or any supported browser without changing the feature files.
Cucumber tests can be integrated into pipelines such as Jenkins or GitHub Actions. By running tests automatically on code commits or scheduled builds, teams get early feedback on potential issues.
Example Jenkins command:
mvn clean test -Dcucumber.options="--tags @Regression"
This executes only the regression scenarios and generates reports automatically, enabling fast feedback and traceability.
Cucumber supports multiple reporting plugins. HTML, JSON, and advanced reports like ExtentReports provide clear insights into scenario execution, failures, and trends. Well-integrated reporting ensures stakeholders can track test results without reading raw logs.
Example plugin configuration in runner:
plugin = {"pretty", "html:target/cucumber-report.html", "json:target/cucumber.json"}
This produces both a human-readable HTML report and a JSON report that can be used for dashboards or CI/CD analytics.
Even with a robust Cucumber and Selenium setup, teams often face challenges that reduce test reliability and maintainability.
Here are some common pitfalls and ways to avoid them:
Integrating Cucumber with Selenium offers a robust framework for behavior-driven development, enabling teams to write tests in natural language while automating browser interactions. This combination enhances collaboration between developers and non-technical stakeholders, ensuring that the application meets business requirements.
For scalable Selenium testing, BrowserStack provides access to over 3,500 real desktop and mobile browsers. It supports parallel test execution, local testing, and seamless integration with CI/CD pipelines, ensuring comprehensive cross-browser compatibility and faster feedback cycles without the need for maintaining an in-house infrastructure.
Run Selenium Tests on Cloud
Get visual proof, steps to reproduce and technical logs with one click
Continue reading
Try Bird on your next bug - you’ll love it
“Game changer”
Julie, Head of QA
Try Bird later, from your desktop