Serenity BDD (formerly Thucydides) is a powerful open-source library that enhances behavior-driven development by providing exceptional reporting and living documentation capabilities. According to the 2024 JVM Ecosystem Report by JRebel, BDD frameworks are used by 34% of Java development teams, with Cucumber being the most popular. Serenity extends Cucumber and JBehave with stakeholder-friendly HTML reports, full screenshot histories, and requirement traceability—transforming raw test results into documentation that product owners and managers can read without technical knowledge. The Screenplay pattern, Serenity’s alternative to Page Objects, addresses the maintainability problems that make large Selenium test suites brittle over time. According to Serenity’s own case studies, teams adopting Screenplay report 40-60% reduction in test maintenance time due to better separation of concerns between user goals and UI interactions. This guide explores Serenity’s integration with BDD frameworks, the Screenplay pattern architecture, and its industry-leading reporting capabilities.

TL;DR

  • Serenity BDD wraps Cucumber/JBehave with rich HTML reports, screenshot history, and requirement traceability
  • The Screenplay pattern replaces brittle Page Objects with actor-centric Tasks, Actions, and Questions
  • Integrates with JUnit 5, TestNG, Maven, and Gradle — no separate report server required
  • Living documentation means non-technical stakeholders can read test results as plain English

Best for: Java teams practicing BDD who need stakeholder-readable reports and requirement coverage Skip if: You don’t use BDD/Gherkin or prefer lightweight tools like Allure with plain Selenium

Introduction to Serenity BDD

“Serenity’s real value isn’t just reporting—it’s the Screenplay pattern. When I see teams writing 500-line Page Objects with dozens of methods, I know maintenance costs are going to explode. Screenplay forces you to think in terms of what users actually do, not what buttons exist on a page, which produces test code that survives UI redesigns far better.” — Yuri Kan, Senior QA Lead

Living Documentation: Tests as Documentation

Serenity automatically generates rich, narrative reports that serve as living documentation of your system’s behavior.

Project Setup

<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>net.serenity-bdd</groupId>
 (as discussed in [Gauge Framework Guide: Language-Independent BDD Alternative to Cucumber](/blog/gauge-framework-guide))        <artifactId>serenity-core</artifactId>
        <version>4.0.30</version>
    </dependency>
    <dependency>
        <groupId>net.serenity-bdd</groupId>
        <artifactId>serenity-cucumber</artifactId>
        <version>4.0.30</version>
    </dependency>
    <dependency>
        <groupId>net.serenity-bdd</groupId>
        <artifactId>serenity-screenplay</artifactId>
        <version>4.0.30</version>
    </dependency>
    <dependency>
        <groupId>net.serenity-bdd</groupId>
        <artifactId>serenity-screenplay-webdriver</artifactId>
        <version>4.0.30</version>
    </dependency>
</dependencies>

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

Serenity with Cucumber Integration

// src/test/java/runners/CucumberTestSuite.java
@RunWith(CucumberWithSerenity.class)
@CucumberOptions(
    features = "src/test/resources/features",
    glue = "steps",
    tags = "@smoke or @regression"
)
public class CucumberTestSuite {}
// Step definitions with Serenity
public class AuthenticationSteps {
    @Steps
    AuthenticationActions authActions;

    @Given("I am on the login page")
    public void navigateToLogin() {
        authActions.navigateToLoginPage();
    }

    @When("I login with username {string} and password {string}")
    public void loginWithCredentials(String username, String password) {
        authActions.enterCredentials(username, password);
        authActions.clickLoginButton();
    }

    @Then("I should see the dashboard")
    public void verifyDashboard() {
        authActions.verifyDashboardDisplayed();
    }
}

Step Libraries

public class AuthenticationActions {
    LoginPage loginPage;
    DashboardPage dashboardPage;

    @Step("Navigate to login page")
    public void navigateToLoginPage() {
        loginPage.open();
    }

    @Step("Enter credentials: {0} / {1}")
    public void enterCredentials(String username, String password) {
        loginPage.enterUsername(username);
        loginPage.enterPassword(password);
    }

    @Step("Click login button")
    public void clickLoginButton() {
        loginPage.clickLogin();
    }

    @Step("Verify dashboard is displayed")
    public void verifyDashboardDisplayed() {
        dashboardPage.shouldBeVisible();
    }
}

Screenplay Pattern: Actor-Centric Testing

The Screenplay pattern provides a more maintainable and expressive approach to test automation.

Screenplay Components

// Actors
public class Actors implements Cast {
    @Override
    public Actor actorNamed(String actorName, WebDriver driver) {
        return Actor.named(actorName)
            .whoCan(BrowseTheWeb.with(driver));
    }
}

// Tasks
public class Login implements Task {
    private final String username;
    private final String password;

    public static Login withCredentials(String username, String password) {
        return new Login(username, password);
    }

    @Override
    @Step("{0} logs in with username #username")
    public <T extends Actor> void performAs(T actor) {
        actor.attemptsTo(
            Open.browserOn().the(LoginPage.class),
            Enter.theValue(username).into(LoginPage.USERNAME_FIELD),
            Enter.theValue(password).into(LoginPage.PASSWORD_FIELD),
            Click.on(LoginPage.LOGIN_BUTTON)
        );
    }
}

// Questions
public class DashboardVisibility implements Question<Boolean> {
    public static DashboardVisibility isDisplayed() {
        return new DashboardVisibility();
    }

    @Override
    public Boolean answeredBy(Actor actor) {
        return actor.asksFor(
            WebElementQuestion.the(DashboardPage.DASHBOARD_CONTAINER)
                .isVisible()
        );
    }
}

// Interactions
public class AddToCart implements Interaction {
    private final String productName;

    public static AddToCart product(String productName) {
        return new AddToCart(productName);
    }

    @Override
    @Step("{0} adds #productName to cart")
    public <T extends Actor> void performAs(T actor) {
        actor.attemptsTo(
            Click.on(ProductPage.addToCartButton(productName)),
            WaitUntil.the(CartPage.CONFIRMATION_MESSAGE, isVisible())
        );
    }
}

Screenplay Test Example

@RunWith(SerenityRunner.class)
public class ShoppingCartTest {

    @Managed(driver = "chrome")
    WebDriver browser;

    Actor customer = Actor.named("Customer");

    @Before
    public void setUp() {
        customer.can(BrowseTheWeb.with(browser));
    }

    @Test
    public void customer_can_add_products_to_cart() {
        givenThat(customer).wasAbleTo(
            NavigateTo.theHomePage(),
            Login.withCredentials("user@example.com", "password123")
        );

        when(customer).attemptsTo(
            SearchFor.product("Laptop"),
            AddToCart.product("Laptop Pro 15"),
            AddToCart.product("Wireless Mouse")
        );

        then(customer).should(
            seeThat(TheCartCount.value(), equalTo(2)),
            seeThat(TheCartTotal.value(), equalTo("$1,029.98"))
        );
    }
}

Report Features Comparison

FeatureSerenity BDDCucumber HTMLExtent Reports
Living DocumentationExcellentBasicGood
ScreenshotsAutomaticManualManual
Test HistoryYesNoYes
Requirements TracingYesNoLimited
Tag FilteringAdvancedBasicGood
Performance MetricsYesNoLimited
Stakeholder-FriendlyExcellentPoorGood

JBehave Integration

// JBehave configuration
public class MyStories extends SerenityStories {

    @Override
    public Configuration configuration() {
        return new MostUsefulConfiguration()
            .useStoryLoader(new LoadFromClasspath(this.getClass()))
            .useStoryReporterBuilder(
                new StoryReporterBuilder()
                    .withDefaultFormats()
                    .withFormats(CONSOLE, HTML, JSON)
            );
    }
}

Advanced Reporting Features

Requirements Hierarchy

@Narrative(
    title = "E-Commerce Shopping Features",
    text = {"As a customer",
            "I want to browse and purchase products",
            "So that I can receive items at home"},
    cardNumber = "SHOP-123"
)
@WithTag("epic:shopping")
public class ShoppingFeatures {}

Custom Report Data

@Step
public void verifyProductDetails(String productName) {
    Serenity.recordReportData()
        .withTitle("Product Details")
        .andContents(productName);

    Serenity.setSessionVariable("productName")
        .to(productName);
}

Screenshot Capture

// Automatic on failure
@Step
public void performCriticalAction() {
    // Screenshots taken automatically on failure
}

// Manual screenshot
@Step
public void captureEvidence() {
    Serenity.takeScreenshot();
}

Practical Use Case: E-Commerce Flow

@RunWith(SerenityRunner.class)
@WithTag("epic:checkout")
public class CheckoutJourneyTest {

    Actor customer = Actor.named("Premium Customer");

    @Test
    @WithTag("type:smoke")
    @Title("Complete purchase with saved payment method")
    public void complete_purchase_with_saved_payment() {
        givenThat(customer).wasAbleTo(
            Login.asRegisteredUser("premium@example.com", "pass123"),
            NavigateTo.theProductCatalog()
        );

        when(customer).attemptsTo(
            SearchFor.product("Laptop"),
            AddToCart.product("Laptop Pro 15").withQuantity(1),
            AddToCart.product("Wireless Mouse").withQuantity(2),
            ProceedTo.checkout(),
            SelectShippingAddress.saved("Home"),
            SelectPaymentMethod.saved("Visa ending 1234"),
            ApplyCoupon.code("SAVE10"),
            ConfirmOrder.withAgreement()
        );

        then(customer).should(
            seeThat("Order is confirmed",
                OrderConfirmation.status(), equalTo("CONFIRMED")),
            seeThat("Discount applied",
                OrderTotal.afterDiscount(), equalTo("$926.98")),
            seeThat("Confirmation email sent",
                EmailNotification.wasSent(), is(true))
        );
    }
}

Best Practices

1. Organize Step Libraries

// Domain-focused step libraries
public class ProductCatalogActions {
    @Step("Search for product: {0}")
    public void searchProduct(String name) { }

    @Step("Filter by category: {0}")
    public void filterByCategory(String category) { }
}

public class CheckoutActions {
    @Step("Select shipping method: {0}")
    public void selectShipping(String method) { }

    @Step("Apply discount code: {0}")
    public void applyDiscount(String code) { }
}

2. Use Meaningful Step Names

// Good: Descriptive business language
@Step("Customer adds {0} to shopping cart")
public void addToCart(String product) { }

// Bad: Technical implementation detail
@Step("Click element with ID 'add-to-cart-btn'")
public void clickAddButton() { }

3. Leverage Tags for Organization

@WithTag("epic:authentication")
@WithTag("capability:login")
@WithTag("type:smoke")
public class LoginTest { }

Conclusion

Serenity BDD elevates behavior-driven development by transforming tests into comprehensive living documentation. Its seamless integration with Cucumber and JBehave, combined with the powerful Screenplay pattern and rich reporting capabilities, makes it an excellent choice for teams seeking stakeholder-friendly test automation.

Key advantages:

  • Living Documentation: Automatic generation of narrative reports
  • Screenplay Pattern: Maintainable, actor-centric test design
  • Rich Reporting: Screenshots, history, and requirement tracing
  • Framework Integration: Works with Cucumber, JBehave, JUnit
  • Stakeholder Value: Non-technical readable reports

For teams prioritizing communication, traceability, and comprehensive documentation alongside test automation, Serenity BDD provides an unmatched solution that bridges the gap between technical testing and business understanding.

FAQ

What is Serenity BDD and how does it differ from plain Cucumber?

Serenity BDD is an open-source reporting and documentation layer built on top of Cucumber, JBehave, or plain JUnit/TestNG. While Cucumber runs Gherkin scenarios, Serenity adds rich HTML reports with screenshots, step histories, requirement traceability, and the Screenplay pattern for actor-centric test design. The result is living documentation that non-technical stakeholders can read.

Does Serenity BDD support Playwright or Selenium?

Serenity BDD supports Selenium WebDriver natively through serenity-webdriver. Playwright support is available via third-party adapters but is not yet officially first-class. For pure Playwright projects, Allure or built-in Playwright reporter is a more natural fit.

What is the Screenplay pattern in Serenity BDD?

Screenplay is an actor-centric design pattern where tests are written as interactions performed by Actors using Abilities. Instead of Page Objects, you create Tasks (user goals), Actions (UI interactions), and Questions (state verification). This results in more readable, composable, and maintainable test code. According to Serenity BDD documentation, teams adopting Screenplay report significantly reduced test maintenance overhead.

Is Serenity BDD free to use commercially?

Yes, Serenity BDD is fully open-source under the Apache 2.0 license. The core library, Screenplay support, Cucumber/JBehave integration, and HTML reporting are all free. Serenity/JS (the JavaScript port) is similarly open-source.

Official Resources

See Also