Cucumber Integration with Selenium: Automating Web Tests

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?


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:

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:

Browser Behavior:


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:

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:


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


Tips for Beginners

🤖
PrepCampusPlus AI Tutor
Scroll to Top