Once a team has a working Cucumber framework, the next problems are scale and value: step definitions that grow unmanageable, feature files nobody reads, and no way to measure whether BDD helps. This guide covers the Screenplay pattern, living documentation and three amigos, custom plugins, and BDD metrics.

Screenplay Pattern with Cucumber

The Screenplay Pattern is an advanced alternative to Page Object Model. It models tests as Actors performing Tasks using Abilities and asking Questions. It produces more readable, maintainable, and reusable test code at scale.

// Core concepts:
// Actor  — represents the user (e.g., Ravi the shopper)
// Ability— what the actor can do (e.g., BrowseTheWeb)
// Task   — business-level action (e.g., Login, AddToCart)
// Action — low-level action (Click, Enter, Navigate)
// Question— asks about the system state (Text of element)
// Actor setup
Actor ravi = Actor.named("Ravi").whoCan(BrowseTheWeb.with(driver));
// Task class
public class Login implements Task {
    private String username, password;
    public static Login withCredentials(String u, String p) {
        Login task = new Login();
        task.username = u; task.password = p;
        return task;
    }
    @Override
    public <T extends Actor> void performAs(T actor) {
        actor.attemptsTo(
            Enter.theValue(username).into(LOGIN_USERNAME),
            Enter.theValue(password).into(LOGIN_PASSWORD),
            Click.on(LOGIN_BUTTON)
        );
    }
}
// Step definition using Screenplay
@When("I log in as {string} with password {string}")
public void loginAs(String user, String pass) {
    actor.attemptsTo(Login.withCredentials(user, pass));
}
@Then("I should see the {string} page")
public void verifyPage(String pageName) {
    assertThat(actor.asksAbout(CurrentPageTitle.displayed()),
               containsString(pageName));
}
Advertisement

Living Documentation & Three Amigos

Three Amigos — the BDD collaboration model

Three Amigos is a meeting/practice where three key roles collaborate BEFORE development begins to define behaviour using concrete examples:

  • Product Owner / Business Analyst — defines WHAT to build, provides acceptance criteria
  • Developer — explains HOW it will be built, raises technical constraints
  • QA / SDET — asks edge case questions, writes the Gherkin scenarios

The output of Three Amigos is a set of agreed Gherkin scenarios that serve as both the specification and the automated acceptance test. This is 'Specification by Example'.

Living Documentation principles

Good BDD scenarios are:
  ✓  Written in business language (no technical jargon)
  ✓  Short — 3 to 7 steps maximum
  ✓  Declarative (WHAT) not imperative (HOW)
  ✓  Independent — don't rely on previous scenario state
  ✓  Single behaviour per scenario
  ✓  Updated when the feature changes (living)
BAD — imperative (too much HOW):
  When I click the element with id 'loginBtn'
  Then the element with class 'dashboard-header' should be visible
GOOD — declarative (focuses on WHAT):
  When I log in with valid credentials
  Then I should see my dashboard
BAD — too many steps:
  Given I open Chrome
  And I type 'https://app.com' in the address bar
  And I press Enter
  And I wait for the page to load
  ...
GOOD — concise precondition:
  Given I am on the login page

Custom Cucumber Plugin / Event Listener

import io.cucumber.plugin.EventListener;
import io.cucumber.plugin.event.*;
// Custom plugin — logs every step with timing
public class StepTimingPlugin implements EventListener {
    @Override
    public void setEventPublisher(EventPublisher publisher) {
        publisher.registerHandlerFor(TestStepStarted.class,   this::onStepStart);
        publisher.registerHandlerFor(TestStepFinished.class,  this::onStepFinish);
        publisher.registerHandlerFor(TestRunFinished.class,   this::onRunFinish);
    }
    private long stepStart;
    private void onStepStart(TestStepStarted event) {
        stepStart = System.currentTimeMillis();
        if (event.getTestStep() instanceof PickleStepTestStep) {
            PickleStepTestStep step = (PickleStepTestStep) event.getTestStep();
            System.out.println("START: " + step.getStep().getText());
        }
    }
    private void onStepFinish(TestStepFinished event) {
        long duration = System.currentTimeMillis() - stepStart;
        System.out.println("DONE in " + duration + "ms — " + event.getResult().getStatus());
    }
    private void onRunFinish(TestRunFinished event) {
        System.out.println("Test run complete: " + event.getResult().getStatus());
    }
}
// Register in @CucumberOptions:
plugin = {"com.example.StepTimingPlugin"}

BDD Metrics & Quality Gates

MetricTargetHow to measure
Scenario pass rate≥ 95%Cucumber JSON → CI dashboard
Flaky scenario rate< 2%Track intermittent failures over 30 days
Scenario execution time< 30s avgCucumber duration plugin
Feature coverage100% of user storiesTag each scenario with @STORY-ID
Undefined steps in CI0Strict mode in runner
Maintenance cost< 10% sprint capacityTrack hours spent fixing broken tests
Documentation freshnessReviewed each sprintFeature file last-modified date in Git
Bug detection rate≥ 60% bugs caught pre-prodDefects found by BDD vs production

Advanced BDD Patterns

Fluent Step Builder pattern

// Instead of many steps for one operation, use a builder in the step
Scenario: Place an order
  Given I have the following order details:
    | product  | iPhone 15 Pro |
    | quantity | 2             |
    | coupon   | SAVE10        |
  When I place the order
  Then the order should be confirmed
// Step def — builds Order object from Data Table
@Given("I have the following order details:")
public void prepareOrder(Map<String, String> details) {
    testContext.setOrder(
        Order.builder()
            .product(details.get("product"))
            .quantity(Integer.parseInt(details.get("quantity")))
            .coupon(details.get("coupon"))
            .build()
    );
}

Event-driven BDD pattern

# Testing event-driven systems
Scenario: Order placed event triggers inventory deduction
  Given the inventory for 'iPhone 15 Pro' is 50
  When an OrderPlaced event is published for 2 units of 'iPhone 15 Pro'
  Then the inventory should be updated to 48 within 5 seconds
// Polling assertion in step def
@Then("the inventory should be updated to {int} within {int} seconds")
public void verifyInventory(int expected, int timeout) {
    await().atMost(timeout, SECONDS)
           .pollInterval(500, MILLISECONDS)
           .until(() -> inventoryService.getCount("iPhone 15 Pro") == expected);
}
// Uses Awaitility library for async assertions

★ Milestone: Design and present a BDD strategy for a 10-person team, including test pyramid ratios, Three Amigos process, CI/CD integration, and quality gate metrics.

FAQs

What is the Screenplay pattern?

A way to structure tests around actors who have abilities, perform tasks and ask questions, instead of page objects; it keeps large suites readable by composing small, reusable tasks. Serenity BDD provides a popular implementation.

What is living documentation in BDD?

Feature files that are kept up to date because they are executed: the published scenarios and their results describe what the system actually does today.