Serenity BDD + Cucumber: Living Documentation for Your Tests

Serenity BDD + Cucumber: Living Documentation for Your Tests

Serenity BDD's most complete form combines Cucumber feature files with Serenity's reporting engine. Feature files provide business-readable specifications. Serenity turns each scenario into a detailed test report with screenshots, step results, and requirements traceability. The result is documentation that's always up to date because it's generated from the tests themselves.

Maven Dependencies

<dependency>
    <groupId>net.serenity-bdd</groupId>
    <artifactId>serenity-core</artifactId>
    <version>4.1.20</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>net.serenity-bdd</groupId>
    <artifactId>serenity-cucumber</artifactId>
    <version>4.1.20</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-java</artifactId>
    <version>7.15.0</version>
    <scope>test</scope>
</dependency>

Feature Files

Feature files are plain Gherkin — Serenity doesn't require any special syntax:

src/test/resources/features/authentication/login.feature:

@authentication
Feature: User Login
  As a registered user
  I want to log in to my account
  So that I can access my dashboard

  Background:
    Given the application is running

  @smoke @happy-path
  Scenario: Successful login with valid credentials
    Given Alice is on the login page
    When she enters her email "alice@example.com" and password "password123"
    And she clicks the Sign In button
    Then she should be redirected to the dashboard
    And she should see the welcome message "Welcome, Alice"

  @negative
  Scenario: Login fails with wrong password
    Given Alice is on the login page
    When she enters her email "alice@example.com" and password "wrongpassword"
    And she clicks the Sign In button
    Then she should see the error message "Invalid credentials"
    And she should remain on the login page

  @negative
  Scenario Outline: Login fails with missing fields
    Given Alice is on the login page
    When she enters her email "<email>" and password "<password>"
    And she clicks the Sign In button
    Then she should see the validation error "<error>"

    Examples:
      | email             | password | error                |
      |                   | pass123  | Email is required    |
      | alice@example.com |          | Password is required |

Cucumber Runner

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

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

The SerenityReporterParallel plugin hooks Cucumber into Serenity's reporting engine.

Step Definitions

package com.example.steps;

import io.cucumber.java.en.*;
import net.thucydides.core.annotations.Steps;
import com.example.stepdefinitions.LoginActions;

public class LoginStepDefinitions {

    @Steps
    LoginActions loginActions;

    @Given("the application is running")
    public void theApplicationIsRunning() {
        // Verify app is accessible — or just proceed
    }

    @Given("{word} is on the login page")
    public void userIsOnLoginPage(String userName) {
        loginActions.openLoginPage();
    }

    @When("she enters her email {string} and password {string}")
    public void entersCredentials(String email, String password) {
        loginActions.enterCredentials(email, password);
    }

    @When("she clicks the Sign In button")
    public void clicksSignIn() {
        loginActions.clickSignIn();
    }

    @Then("she should be redirected to the dashboard")
    public void redirectedToDashboard() {
        loginActions.verifyOnDashboard();
    }

    @Then("she should see the welcome message {string}")
    public void seesWelcomeMessage(String message) {
        loginActions.verifyWelcomeMessage(message);
    }

    @Then("she should see the error message {string}")
    public void seesErrorMessage(String message) {
        loginActions.verifyErrorMessage(message);
    }

    @Then("she should remain on the login page")
    public void remainsOnLoginPage() {
        loginActions.verifyOnLoginPage();
    }

    @Then("she should see the validation error {string}")
    public void seesValidationError(String error) {
        loginActions.verifyValidationError(error);
    }
}

Action Classes (Step Libraries)

Keep step definitions thin — delegate to action classes with @Step annotations:

package com.example.stepdefinitions;

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

public class LoginActions {

    LoginPage loginPage;
    DashboardPage dashboardPage;

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

    @Step("Enter credentials: {0}")
    public void enterCredentials(String email, String password) {
        loginPage.enterEmail(email);
        loginPage.enterPassword(password);
    }

    @Step("Click the Sign In button")
    public void clickSignIn() {
        loginPage.clickSignIn();
    }

    @Step("Verify user is on the dashboard")
    public void verifyOnDashboard() {
        dashboardPage.shouldBeDisplayed();
    }

    @Step("Verify welcome message: {0}")
    public void verifyWelcomeMessage(String message) {
        dashboardPage.welcomeMessage().shouldContainText(message);
    }

    @Step("Verify error message: {0}")
    public void verifyErrorMessage(String message) {
        loginPage.errorMessage().shouldContainText(message);
    }

    @Step("Verify user remains on login page")
    public void verifyOnLoginPage() {
        loginPage.shouldBeDisplayed();
    }

    @Step("Verify validation error: {0}")
    public void verifyValidationError(String error) {
        loginPage.validationError().shouldContainText(error);
    }
}

Running with Tags

# Run all tests
mvn clean verify

# Run only smoke tests
mvn clean verify -Dcucumber.filter.tags="@smoke"

# Run authentication tests excluding negative cases
mvn clean verify -Dcucumber.filter.tags="@authentication and not @negative"

# Run in CI with base URL
mvn clean verify -Denvironment=ci -DBASE_URL=https://staging.example.com

Parallel Execution

Enable parallel Cucumber execution in junit-platform.properties:

cucumber.execution.parallel.enabled=true
cucumber.execution.parallel.config.strategy=fixed
cucumber.execution.parallel.config.fixed.parallelism=4

Serenity handles thread-safe WebDriver instances per scenario automatically.

The Report

After mvn clean verify, the report is at target/site/serenity/index.html.

The report structure:

  • Test results — pass/fail per scenario, with full step details
  • Requirements — features and stories derived from feature file organization and tags
  • Test coverage — which requirements have passing tests
  • Failures — screenshot + page source at point of failure

Each scenario in the report shows every Cucumber step, every @Step action within it, and a screenshot at key points. Product owners can read the feature file, then open the report to see it executed.

File Organization

src/test/
  java/
    com/example/
      pages/           # PageObject classes
      steps/           # @Step action libraries
      stepdefinitions/ # Cucumber step definitions (thin)
      runners/         # CucumberTestSuite
  resources/
    features/
      authentication/  # login.feature, logout.feature
      checkout/        # cart.feature, payment.feature
    serenity.conf

Feature files organized by domain (not by test type) map cleanly to Serenity's requirements hierarchy.

Summary

Serenity + Cucumber produces test automation that serves two audiences: developers write and maintain the step definitions and action classes, stakeholders read the feature files and HTML reports. The living documentation angle is real — the report is always generated from the same code that runs in CI, so it can't drift from what's actually tested.

The main cost is setup complexity. You're maintaining feature files, step definitions, action classes, and page objects. For smaller projects, plain JUnit + Serenity without Cucumber is simpler. For teams where BDD collaboration is the goal, the full Cucumber stack is worth it.

Read more

Start now free