Welcome to the eighteenth part of our Cucumber series for beginners! In the previous post, we explored the Test Runner, which orchestrates Cucumber test execution. Now, we’ll dive into Integration with Selenium, a powerful combination that allows you to automate web browser interactions using Cucumber’s behavior-driven development (BDD) approach. This guide will explain how to integrate Cucumber with Selenium, set up a project, and provide practical examples to make it easy for beginners and valuable for experienced professionals. Let’s get started!


What is Cucumber Integration with Selenium?

Cucumber with Selenium combines Cucumber’s human-readable Gherkin scenarios with Selenium WebDriver’s ability to automate web browsers. Cucumber defines test scenarios in plain language, while Selenium executes browser actions like clicking buttons, entering text, or verifying page elements. This integration enables end-to-end testing of web applications in a collaborative, maintainable way.

Why Use Cucumber with Selenium?

  • Readable Tests: Write tests in Gherkin that non-technical stakeholders can understand.
  • Browser Automation: Use Selenium to interact with web elements (e.g., forms, links, tables).
  • Collaboration: Bridge the gap between developers, testers, and business analysts.
  • Reusability: Create reusable step definitions for common web interactions.
  • Cross-Browser Testing: Test on multiple browsers (e.g., Chrome, Firefox) using Selenium.

Setting Up a Cucumber-Selenium Project

Let’s set up a Maven project, create a feature file, and integrate Selenium to automate a login scenario. This example uses ChromeDriver, but you can adapt it for other browsers.

Step 1: Create a Maven Project

Create a new Maven project in your IDE (e.g., IntelliJ) with the following pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>cucumber-selenium-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- Cucumber Dependencies -->
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>7.18.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>7.18.0</version>
            <scope>test</scope>
        </dependency>
        <!-- Selenium Dependency -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.25.0</version>
            <scope>test</scope>
        </dependency>
        <!-- JUnit Dependency -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.5.0</version>
            </plugin>
        </plugins>
    </build>
</project>

Step 2: Download ChromeDriver

Download the ChromeDriver executable compatible with your Chrome browser version from chromedriver.chromium.org. Place it in a directory (e.g., drivers/) in your project or set the path programmatically.

Step 3: Create a Feature File

Create a file named login.feature in src/test/resources/features:

Feature: User Login
  As a user, I want to log in to the application so that I can access my account.

  Scenario: Successful login with valid credentials
    Given the user is on the login page
    When the user enters "demo@example.com" and "password123"
    And the user clicks the login button
    Then the user should be redirected to the dashboard

Note: This example uses a demo website (e.g., saucedemo.com) for testing. Replace URLs and credentials with your application’s details.

Step 4: Create Step Definitions with Selenium

Create LoginSteps.java in src/test/java/steps:

package steps;

import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.And;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.junit.Assert;

public class LoginSteps {
    private WebDriver driver;

    @Before
    public void setUp() {
        System.setProperty("webdriver.chrome.driver", "drivers/chromedriver"); // Update path
        driver = new ChromeDriver();
        driver.manage().window().maximize();
    }

    @After
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }

    @Given("the user is on the login page")
    public void userIsOnLoginPage() {
        driver.get("https://www.saucedemo.com/");
    }

    @When("the user enters {string} and {string}")
    public void userEntersCredentials(String username, String password) {
        driver.findElement(By.id("user-name")).sendKeys(username);
        driver.findElement(By.id("password")).sendKeys(password);
    }

    @And("the user clicks the login button")
    public void userClicksLoginButton() {
        driver.findElement(By.id("login-button")).click();
    }

    @Then("the user should be redirected to the dashboard")
    public void userRedirectedToDashboard() {
        String currentUrl = driver.getCurrentUrl();
        Assert.assertTrue("User not redirected to dashboard", currentUrl.contains("inventory.html"));
    }
}

Explanation:

  • @Before: Initializes ChromeDriver and maximizes the browser window.
  • @After: Closes the browser after each scenario.
  • Selenium Actions:
    • Navigates to the login page (driver.get).
    • Enters credentials using findElement and sendKeys.
    • Clicks the login button.
    • Verifies redirection by checking the URL.
  • Assertions: Uses JUnit’s Assert to verify the outcome.

Step 5: Create a Test Runner

Create TestRunner.java in src/test/java/runner:

package runner;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(
    features = "src/test/resources/features",
    glue = "steps",
    plugin = {
        "pretty",
        "html:target/cucumber-reports/cucumber.html",
        "json:target/cucumber-reports/cucumber.json"
    },
    monochrome = true
)
public class TestRunner {
}

Step 6: Run the Tests

Ensure ChromeDriver is in the specified path (e.g., drivers/chromedriver). Run the tests using Maven:

mvn test

Output (Console):

Navigating to login page
Entering username: demo@example.com, password: password123
Clicking login button
Verifying redirection to dashboard

1 Scenarios (1 passed)
4 Steps (4 passed)
0m5.123s

Reports:

  • HTML: Open target/cucumber-reports/cucumber.html in a browser to view the test results.
  • JSON: Check target/cucumber-reports/cucumber.json for structured data.

Browser Behavior:

  • A Chrome browser opens, navigates to saucedemo.com, enters credentials, clicks the login button, and verifies the dashboard URL before closing.

Enhancing the Integration

Let’s explore advanced techniques to make the Cucumber-Selenium integration more robust.

Example 1: Handling Multiple Scenarios

Update login.feature to include a failed login scenario:

Feature: User Login
  As a user, I want to log in to the application so that I can access my account.

  @smoke
  Scenario: Successful login with valid credentials
    Given the user is on the login page
    When the user enters "standard_user" and "secret_sauce"
    And the user clicks the login button
    Then the user should be redirected to the dashboard

  @regression
  Scenario: Failed login with invalid credentials
    Given the user is on the login page
    When the user enters "invalid_user" and "wrongpass"
    And the user clicks the login button
    Then the user should see an error message

Update LoginSteps.java:

@Then("the user should see an error message")
public void userSeesErrorMessage() {
    String errorMessage = driver.findElement(By.cssSelector("[data-test='error']")).getText();
    Assert.assertTrue("Error message not displayed", errorMessage.contains("Username and password do not match"));
}

Run the tests:

mvn test

Output:
The HTML report will show one passed scenario (@smoke) and one passed scenario (@regression), assuming the error message is displayed correctly.

Example 2: Using Scenario Hooks for Screenshots

Capture screenshots on failure using an @After hook. Update LoginSteps.java:

import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import io.cucumber.java.Scenario;
import java.nio.file.Files;
import java.nio.file.Paths;

@After
public void tearDown(Scenario scenario) {
    if (scenario.isFailed() && driver != null) {
        byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
        scenario.attach(screenshot, "image/png", "failure-screenshot");
    }
    if (driver != null) {
        driver.quit();
    }
}

Explanation:

  • If a scenario fails, a screenshot is captured and attached to the Cucumber report.
  • The screenshot appears in the HTML report for failed scenarios, aiding debugging.

Example 3: Page Object Model (POM)

Use the Page Object Model to organize web elements and actions. Create LoginPage.java in src/test/java/pages:

package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class LoginPage {
    private WebDriver driver;
    private By usernameField = By.id("user-name");
    private By passwordField = By.id("password");
    private By loginButton = By.id("login-button");
    private By errorMessage = By.cssSelector("[data-test='error']");

    public LoginPage(WebDriver driver) {
        this.driver = driver;
    }

    public void enterCredentials(String username, String password) {
        driver.findElement(usernameField).sendKeys(username);
        driver.findElement(passwordField).sendKeys(password);
    }

    public void clickLoginButton() {
        driver.findElement(loginButton).click();
    }

    public String getErrorMessage() {
        return driver.findElement(errorMessage).getText();
    }

    public String getCurrentUrl() {
        return driver.getCurrentUrl();
    }
}

Update LoginSteps.java to use the page object:

public class LoginSteps {
    private WebDriver driver;
    private LoginPage loginPage;

    @Before
    public void setUp() {
        System.setProperty("webdriver.chrome.driver", "drivers/chromedriver");
        driver = new ChromeDriver();
        driver.manage().window().maximize();
        loginPage = new LoginPage(driver);
    }

    @Given("the user is on the login page")
    public void userIsOnLoginPage() {
        driver.get("https://www.saucedemo.com/");
    }

    @When("the user enters {string} and {string}")
    public void userEntersCredentials(String username, String password) {
        loginPage.enterCredentials(username, password);
    }

    @And("the user clicks the login button")
    public void userClicksLoginButton() {
        loginPage.clickLoginButton();
    }

    @Then("the user should be redirected to the dashboard")
    public void userRedirectedToDashboard() {
        String currentUrl = loginPage.getCurrentUrl();
        Assert.assertTrue("User not redirected to dashboard", currentUrl.contains("inventory.html"));
    }

    @Then("the user should see an error message")
    public void userSeesErrorMessage() {
        String errorMessage = loginPage.getErrorMessage();
        Assert.assertTrue("Error message not displayed", errorMessage.contains("Username and password do not match"));
    }
}

Explanation:

  • POM: Encapsulates web elements and actions in LoginPage, improving maintainability.
  • Reusability: Page methods can be reused across multiple step definitions.

Best Practices for Cucumber-Selenium Integration

  1. Use Page Object Model: Organize web elements and actions in page classes to reduce duplication.
  2. Manage WebDriver: Initialize and close WebDriver in @Before and @After hooks.
  3. Add Screenshots: Capture screenshots on failure for debugging.
  4. Use Explicit Waits: Use WebDriverWait for dynamic elements to avoid flaky tests:
    import org.openqa.selenium.support.ui.WebDriverWait;
    import org.openqa.selenium.support.ui.ExpectedConditions;
    
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("login-button")));
    
  5. Keep Steps Focused: Write Gherkin steps for user behavior, not implementation details.
  6. Test Across Browsers: Configure WebDriver for multiple browsers (e.g., Chrome, Firefox) using a factory pattern.

Troubleshooting Cucumber-Selenium Issues

  • WebDriver Path Errors: Ensure the ChromeDriver path is correct and matches your browser version.
  • Element Not Found: Use explicit waits or verify element locators (e.g., ID, CSS, XPath).
  • Flaky Tests: Add synchronization (e.g., WebDriverWait) to handle dynamic content.
  • Browser Not Closing: Ensure @After hook calls driver.quit().
  • Report Issues: Verify plugin settings in TestRunner for HTML/JSON report generation.

Tips for Beginners

  • Start with One Browser: Use ChromeDriver initially before adding cross-browser support.
  • Inspect Elements: Use browser developer tools to find reliable locators (e.g., ID, CSS).
  • Run Tests Locally: Test in your IDE before integrating with CI/CD.
  • Use Saucedemo: Practice with saucedemo.com for learning.

What’s Next?

You’ve learned how to integrate Cucumber with Selenium to automate web browser testing. In the next blog post, we’ll explore Integration with REST-assured, which enables API testing with Cucumber for validating backend services.

Let me know when you’re ready for the next topic (Integration with REST-assured), and I’ll provide a detailed post!

System: * Today's date and time is 04:45 PM IST on Friday, June 06, 2025.