Serenity BDD with Cucumber and JUnit: Complete Setup Guide

Serenity BDD with Cucumber and JUnit: Complete Setup Guide

Serenity BDD integrates with Cucumber to give you something neither framework provides alone: Cucumber's plain-English scenarios plus Serenity's rich HTML reports with screenshots, step-level narrative, and coverage statistics. This guide covers the full setup from scratch.

What Serenity Adds to Cucumber

Cucumber gives you readable scenarios and step definitions. Serenity adds:

  • Narrative HTML reports — not just pass/fail, but step-by-step breakdowns with screenshots at each action
  • WebDriver lifecycle management — automatic browser setup, teardown, and screenshot capture
  • Screenplay pattern support — a more scalable alternative to page objects
  • Requirements coverage — shows which stories and features are tested
  • Living documentation — publishable HTML reports that serve as system documentation

Project Setup

Create a Maven project with these dependencies:

<properties>
    <serenity.version>3.9.8</serenity.version>
    <cucumber.version>7.14.0</cucumber.version>
</properties>

<dependencies>
    <!-- Serenity core -->
    <dependency>
        <groupId>net.serenity-bdd</groupId>
        <artifactId>serenity-core</artifactId>
        <version>${serenity.version}</version>
        <scope>test</scope>
    </dependency>

    <!-- Serenity + Cucumber integration -->
    <dependency>
        <groupId>net.serenity-bdd</groupId>
        <artifactId>serenity-cucumber</artifactId>
        <version>${serenity.version}</version>
        <scope>test</scope>
    </dependency>

    <!-- JUnit 5 runner -->
    <dependency>
        <groupId>net.serenity-bdd</groupId>
        <artifactId>serenity-junit5</artifactId>
        <version>${serenity.version}</version>
        <scope>test</scope>
    </dependency>

    <!-- Serenity WebDriver (for UI tests) -->
    <dependency>
        <groupId>net.serenity-bdd</groupId>
        <artifactId>serenity-webdriver</artifactId>
        <version>${serenity.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Add the Serenity Maven plugin to generate reports after tests run:

<plugin>
    <groupId>net.serenity-bdd.maven.plugins</groupId>
    <artifactId>serenity-maven-plugin</artifactId>
    <version>${serenity.version}</version>
    <executions>
        <execution>
            <id>serenity-reports</id>
            <phase>post-integration-test</phase>
            <goals>
                <goal>aggregate</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Feature Files

Write feature files in src/test/resources/features/:

# src/test/resources/features/user_login.feature
@authentication
Feature: User Login

  Background:
    Given the login page is open

  Scenario: Successful login with valid credentials
    When the user enters username "alice@example.com"
    And the user enters password "SecurePass123!"
    And the user clicks the login button
    Then the user should be redirected to the dashboard
    And the welcome message should display "Welcome, Alice"

  Scenario: Failed login with wrong password
    When the user enters username "alice@example.com"
    And the user enters password "WrongPassword"
    And the user clicks the login button
    Then an error message should display "Invalid credentials"
    And the user should remain on the login page

  Scenario Outline: Login validation
    When the user enters username "<email>"
    And the user enters password "<password>"
    And the user clicks the login button
    Then the result should be "<result>"

    Examples:
      | email              | password      | result  |
      | alice@example.com  | SecurePass123 | success |
      |                    | SecurePass123 | error   |
      | alice@example.com  |               | error   |

The JUnit 5 Runner

Create a runner class to connect Cucumber with Serenity:

package com.example.tests;

import io.cucumber.junit.platform.engine.Constants;
import net.serenitybdd.cucumber.CucumberWithSerenity;
import org.junit.platform.suite.api.*;

@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("features")
@ConfigurationParameter(
    key = Constants.PLUGIN_PROPERTY_NAME,
    value = "io.cucumber.core.plugin.SerenityReporter"
)
@ConfigurationParameter(
    key = Constants.GLUE_PROPERTY_NAME,
    value = "com.example.steps"
)
public class CucumberTestSuite {}

Page Objects with Serenity

Serenity page objects extend PageObject for automatic WebDriver management:

package com.example.pages;

import net.serenitybdd.core.pages.PageObject;
import net.thucydides.core.annotations.DefaultUrl;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;

@DefaultUrl("https://example.com/login")
public class LoginPage extends PageObject {

    @FindBy(id = "email")
    private WebElement emailField;

    @FindBy(id = "password")
    private WebElement passwordField;

    @FindBy(css = "button[type='submit']")
    private WebElement loginButton;

    @FindBy(css = ".error-message")
    private WebElement errorMessage;

    @FindBy(css = ".welcome-banner")
    private WebElement welcomeBanner;

    public void enterEmail(String email) {
        emailField.clear();
        emailField.sendKeys(email);
    }

    public void enterPassword(String password) {
        passwordField.clear();
        passwordField.sendKeys(password);
    }

    public void clickLogin() {
        loginButton.click();
    }

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

    public String getWelcomeMessage() {
        return welcomeBanner.getText();
    }

    public boolean isOnLoginPage() {
        return getCurrentUrl().contains("/login");
    }
}

Step Definitions

Step definition classes inject Serenity page objects via field injection:

package com.example.steps;

import com.example.pages.LoginPage;
import com.example.pages.DashboardPage;
import io.cucumber.java.en.*;
import net.thucydides.core.annotations.Steps;
import org.assertj.core.api.Assertions;

public class LoginSteps {

    LoginPage loginPage;
    DashboardPage dashboardPage;

    @Given("the login page is open")
    public void openLoginPage() {
        loginPage.open();
    }

    @When("the user enters username {string}")
    public void enterUsername(String username) {
        loginPage.enterEmail(username);
    }

    @When("the user enters password {string}")
    public void enterPassword(String password) {
        loginPage.enterPassword(password);
    }

    @When("the user clicks the login button")
    public void clickLogin() {
        loginPage.clickLogin();
    }

    @Then("the user should be redirected to the dashboard")
    public void verifyDashboardRedirect() {
        dashboardPage.shouldBeCurrentlyOpen();
    }

    @Then("the welcome message should display {string}")
    public void verifyWelcomeMessage(String expected) {
        Assertions.assertThat(dashboardPage.getWelcomeMessage())
            .isEqualTo(expected);
    }

    @Then("an error message should display {string}")
    public void verifyErrorMessage(String expected) {
        Assertions.assertThat(loginPage.getErrorMessage())
            .isEqualTo(expected);
    }

    @Then("the user should remain on the login page")
    public void verifyStillOnLoginPage() {
        Assertions.assertThat(loginPage.isOnLoginPage()).isTrue();
    }
}

Serenity Configuration

Create serenity.conf in src/test/resources/:

serenity {
    project.name = "My Application Tests"
    take.screenshots = FOR_FAILURES  # or AFTER_EACH_STEP
    reports.show.step.details = true
}

webdriver {
    driver = chrome
    autodownload = true
}

headless.mode = true

environments {
    default {
        webdriver.base.url = "https://staging.example.com"
    }
    production {
        webdriver.base.url = "https://example.com"
    }
}

Switch environments at runtime:

mvn verify -Denvironment=production

Running Tests

# Run all tests
mvn verify

# Run specific tag
mvn verify -Dcucumber.filter.tags="@authentication"

# Run with specific browser
mvn verify -Dwebdriver.driver=firefox

# Headless mode
mvn verify -Dheadless=true

Reports appear in target/site/serenity/index.html.

Step Libraries

For reusable actions across multiple step definition classes, create a step library:

package com.example.steps;

import com.example.pages.LoginPage;
import net.thucydides.core.annotations.Step;

public class AuthenticationSteps {

    LoginPage loginPage;

    @Step("Open the login page and sign in as {0}")
    public void loginAs(String email, String password) {
        loginPage.open();
        loginPage.enterEmail(email);
        loginPage.enterPassword(password);
        loginPage.clickLogin();
    }

    @Step("Verify the user is authenticated")
    public boolean isLoggedIn() {
        return !loginPage.isOnLoginPage();
    }
}

Inject step libraries with @Steps:

@Steps
AuthenticationSteps auth;

@Given("the user is logged in as {string}")
public void userIsLoggedIn(String email) {
    auth.loginAs(email, "defaultPassword");
}

The @Step annotation makes each method appear as a named step in Serenity's HTML report.

Hooks and Background Setup

Serenity integrates with Cucumber hooks:

package com.example.steps;

import io.cucumber.java.After;
import io.cucumber.java.Before;
import net.thucydides.core.annotations.Managed;
import org.openqa.selenium.WebDriver;

public class Hooks {

    @Managed
    WebDriver driver;

    @Before("@requires-login")
    public void loginBeforeTest() {
        // Perform login setup
    }

    @After
    public void cleanupAfterTest(io.cucumber.java.Scenario scenario) {
        if (scenario.isFailed()) {
            // Serenity already captures screenshots on failure
            // Add any additional cleanup here
        }
    }
}

CI Integration

# GitHub Actions
name: Serenity BDD Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Run Serenity tests
        run: mvn verify -Dheadless=true

      - name: Upload Serenity reports
        uses: actions/upload-artifact@v3
        if: always()
        with:
          name: serenity-reports
          path: target/site/serenity/

Production Monitoring

Serenity + Cucumber covers acceptance testing in your development pipeline. For ongoing production confidence, HelpMeTest complements this by running your critical user flows continuously — without requiring a Java build, WebDriver, or CI trigger. Use Serenity to specify and verify behavior during development; use HelpMeTest to confirm that production stays healthy between releases.

Summary

Serenity BDD + Cucumber + JUnit gives you readable Gherkin scenarios, automatic WebDriver management, and publishable HTML reports — all from the same test run. The setup is straightforward: dependencies, a runner class, page objects, and step definitions. Once wired together, every test run produces a living document of what your system does and whether it does it correctly.

Read more

Start now free