What is Cucumber with Maven?

Cucumber with Maven involves using Maven, a widely-used build automation tool, to manage Cucumber test projects. Maven simplifies dependency management, test execution, and reporting for Cucumber tests by defining project structure, libraries, and build configurations in a pom.xml file. This integration streamlines development, testing, and CI/CD workflows.

Why Use Cucumber with Maven?

  • Dependency Management: Automatically download and manage libraries (e.g., Cucumber, Selenium).
  • Standardized Structure: Enforce a consistent project layout for scalability.
  • Build Automation: Run tests, generate reports, and package projects with simple commands.
  • CI/CD Integration: Seamlessly integrate with tools like Jenkins or GitLab CI.
  • Customization: Configure test execution with profiles, properties, and plugins.

Setting Up a Cucumber-Maven Project

Let’s create a Maven-based Cucumber project with Selenium to automate a login test on saucedemo.com. We’ll configure Maven to manage dependencies, run tests, and generate reports.

Prerequisites

  1. Install Tools:
    • Java JDK (11 or later).
    • Maven: Download from maven.apache.org and set MAVEN_HOME.
    • IDE: IntelliJ IDEA or Eclipse (optional but recommended).
    • ChromeDriver: Compatible with your Chrome browser, placed in a project directory (e.g., drivers/).
  2. Verify Maven:
    mvn -version
    

Step 1: Create a Maven Project

Create a new Maven project using the command line or your IDE.

Command Line

mvn archetype:generate -DgroupId=com.example -DartifactId=cucumber-maven-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

IDE (IntelliJ)

  1. File > New > Project > Maven.
  2. Set GroupId: com.example, ArtifactId: cucumber-maven-demo.
  3. Finish and open the project.

Step 2: Configure pom.xml

Replace the default pom.xml with the following to include Cucumber, Selenium, and Maven plugins:

<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-maven-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <cucumber.version>7.18.0</cucumber.version>
        <selenium.version>4.25.0</selenium.version>
        <junit.version>4.13.2</junit.version>
    </properties>

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

    <build>
        <plugins>
            <!-- Maven Compiler Plugin -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
            <!-- Maven Surefire Plugin for Test Execution -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.5.0</version>
                <configuration>
                    <includes>
                        <include>**/TestRunner.java</include>
                    </includes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Explanation:

  • Properties: Centralizes version numbers for easy updates.
  • Dependencies: Includes Cucumber, Selenium, and JUnit for testing.
  • Plugins:
    • maven-compiler-plugin: Ensures Java 11 compatibility.
    • maven-surefire-plugin: Runs tests via the TestRunner class.

Step 3: Create Project Structure

Set up the following structure:

cucumber-maven-demo/
├── src/
│   ├── test/
│   │   ├── java/
│   │   │   ├── steps/       # Step definitions and hooks
│   │   │   └── runner/      # TestRunner class
│   │   └── resources/
│   │       └── features/    # Feature files
├── drivers/                 # ChromeDriver
├── reports/                 # Test reports
├── pom.xml

Step 4: Create a Feature File

Create 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.

  @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

Step 5: Create Step Definitions

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.*;
import org.junit.Assert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class LoginSteps {
    private WebDriver driver;

    @Before
    public void setUp() {
        System.setProperty("webdriver.chrome.driver", "drivers/chromedriver");
        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"));
    }
}

Step 6: 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",
    tags = "@smoke",
    plugin = {
        "pretty",
        "html:reports/cucumber.html",
        "json:reports/cucumber.json",
        "junit:reports/cucumber-junit.xml"
    },
    monochrome = true
)
public class TestRunner {
}

Explanation:

  • features: Points to feature files.
  • glue: Specifies step definitions package.
  • tags: Runs only @smoke tests.
  • plugin: Generates HTML, JSON, and JUnit XML reports.
  • monochrome: Ensures clean console output.

Step 7: Run Tests with Maven

Ensure ChromeDriver is in drivers/. Run tests from the project root:

mvn clean test

Output:

[INFO] --- maven-surefire-plugin:3.5.0:test (default-test) @ cucumber-maven-demo ---
...
1 Scenario (1 passed)
4 Steps (4 passed)
0m5.123s
...
[INFO] BUILD SUCCESS

Reports:

  • HTML: reports/cucumber.html (view in browser).
  • JSON: reports/cucumber.json (machine-readable).
  • JUnit XML: reports/cucumber-junit.xml (CI-compatible).

Enhancing Cucumber-Maven Integration

Let’s explore advanced Maven configurations to optimize test execution.

Example 1: Running Specific Tags

Pass tags via Maven to run specific test suites.

Update pom.xml:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.5.0</version>
            <configuration>
                <includes>
                    <include>**/TestRunner.java</include>
                </includes>
                <systemPropertyVariables>
                    <cucumber.filter.tags>${cucumber.tags}</cucumber.filter.tags>
                </systemPropertyVariables>
            </configuration>
        </plugin>
    </plugins>
</build>

Run smoke tests:

mvn clean test -Dcucumber.tags="@smoke"

Why?

  • Allows dynamic tag selection without modifying TestRunner.

Example 2: Maven Profiles for Environments

Use Maven profiles to switch between environments (e.g., dev, prod).

Update pom.xml:

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <base.url>https://dev.saucedemo.com</base.url>
        </properties>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <base.url>https://www.saucedemo.com</base.url>
        </properties>
    </profile>
</profiles>

Update LoginSteps.java:

@Given("the user is on the login page")
public void userIsOnLoginPage() {
    String baseUrl = System.getProperty("base.url", "https://www.saucedemo.com/");
    driver.get(baseUrl);
}

Run tests for dev:

mvn clean test -Pdev

Run tests for prod:

mvn clean test -Pprod

Why?

  • Supports testing across multiple environments.
  • Reduces hardcoding.

Example 3: Parallel Test Execution

Run tests in parallel to reduce execution time using the maven-surefire-plugin.

Update pom.xml:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.5.0</version>
    <configuration>
        <includes>
            <include>**/TestRunner.java</include>
        </includes>
        <parallel>methods</parallel>
        <threadCount>4</threadCount>
    </configuration>
</plugin>

Note: Ensure step definitions are thread-safe (e.g., use Dependency Injection to avoid shared WebDriver instances, as shown in the Dependency Injection post).

Run tests:

mvn clean test

Why?

  • Speeds up large test suites.
  • Requires careful management of shared resources.

Example 4: Adding Third-Party Reports

Integrate ExtentReports for advanced reporting. Add dependency to pom.xml:

<dependency>
    <groupId>tech.grasshopper</groupId>
    <artifactId>extentreports-cucumber7-adapter</artifactId>
    <version>1.14.0</version>
    <scope>test</scope>
</dependency>

Update TestRunner.java:

@CucumberOptions(
    features = "src/test/resources/features",
    glue = "steps",
    tags = "@smoke",
    plugin = {
        "pretty",
        "html:reports/cucumber.html",
        "json:reports/cucumber.json",
        "junit:reports/cucumber-junit.xml",
        "com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"
    },
    monochrome = true
)

Create extent.properties in src/test/resources:

extent.reporter.spark.start=true
extent.reporter.spark.out=reports/ExtentReport.html
screenshot.dir=reports/screenshots/
screenshot.rel.path=screenshots/

Run tests:

mvn clean test

Output:

  • Generates an ExtentReport at reports/ExtentReport.html.

Why?

  • Provides interactive, detailed reports for team reviews.

Best Practices for Cucumber with Maven

  1. Centralize Versions: Use <properties> in pom.xml to manage dependency versions.
  2. Organize Structure: Follow Maven’s standard directory layout (src/test/java, src/test/resources).
  3. Use Profiles: Configure environments or test types with Maven profiles.
  4. Generate Reports: Include HTML, JSON, and JUnit plugins for comprehensive reporting.
  5. Validate Locally: Run mvn clean test locally before CI/CD integration.
  6. Keep Tests Independent: Use DI or hooks to ensure scenario isolation.

Troubleshooting Cucumber-Maven Issues

  • Dependency Errors: Run mvn clean install to download dependencies.
  • Test Not Found: Ensure TestRunner.java is in the correct package and included in surefire-plugin.
  • Report Not Generated: Verify plugin paths in TestRunner (e.g., reports/cucumber.html).
  • Selenium Errors: Confirm ChromeDriver path and version compatibility.
  • Tag Issues: Check -Dcucumber.tags syntax (e.g., @smoke, not smoke).

Tips for Beginners

  • Start Simple: Begin with minimal pom.xml dependencies.
  • Use Maven Commands: Practice mvn clean, mvn test, and mvn install.
  • Check Reports: Open HTML reports to verify test results.
  • Explore Plugins: Experiment with surefire-plugin and reporting plugins.