Cucumber is a popular tool for automating BDD-style acceptance criteria. Teams that practice BDD have conversations about business rules and examples to better understand the problem they are solving. Some of these examples can be transformed into automated acceptance criteria.

Teams that use Cucumber express these acceptance criteria in a natural, human-readable form using the given..when..then structure:

Scenario: Cash withdrawal
  Given Clive has $1000 in his Current account
  When he withdraws $100 in cash
  Then his remaining balance should be $900

This format is known as 'Gherkin', and is widely used in Cucumber and other Cucumber-based BDD tools such as SpecFlow (for .NET) and Behave (for Python). Gherkin is a flexible, highly readable format that can be written collaboratively with product owners to ensure that everyone is on the same page. The loosely-structured 'Given-When-Then' format helps people focus on what they are trying to achieve, and how they will know when they get it.

Gherkin also supports tabular data formats, which is a useful and concise way of representing many domain concepts:

Scenario: Transferring funds between internal accounts
  Given Clive has the following accounts:
    | Account | Balance |
    | Current | 1000    |
    | Savings | 2000    |
  When he transfers $100 from his Current account to his Savings account
  Then his new account balances should be:
    | Account | Balance |
    | Current | 900     |
    | Savings | 2100    |

Sometimes tables can be used to summarise several different examples of the same scenario. For example, the following scenario illustrates the fee structure for different account types:

Scenario Outline: Account Fees for different types of account
  Given Clive has an <Account Type> account with a balance of <Sample Balance>
  When the monthly fees are calculated
  Then a <Monthly Fee> monthly fee of <Sample Fee> should be deducted
  Examples:
  | Account Type | Monthly Fee | Sample Balance | Sample Fee |
  | Current      | $1.50       | 1000           | 1.50       |
  | Savings      | $0.00       | 1000           | 0.00       |
  | Investment   | 0.25%       | 1000           | 2.50       |

Serenity BDD integrates very smoothly with Cucumber. When you implement your Cucumber scenarios with Serenity BDD, you benefit from the powerful Serenity reporting features, which are fine-tuned to work well with Cucumber. But Serenity also makes it easier to write cleaner, more maintainable test automation code.

cucumber cash withdrawals feature
Figure 1. A Cucumber scenario in a Serenity report

Using Cucumber with Serenity BDD

There are currently two commonly-used versions of Cucumber: Cucumber 2 and Cucumber 4. While they both support the same Gherkin notation, the way you implement the steps is a little different in each version. When you use Serenity with Cucumber, you use a different dependency depending on the version of Cucumber you want to use.

New versions come out regularly: you can find out what the latest stable versions are [on the Serenity BDD Github home page](https://github.com/serenity-bdd/serenity-core/#what-is-the-latest-stable-version-i-should-use).

Working with Cucumber 2

To get started with Cucumber 2 and Serenity, you will need to add the Serenity Cucumber plugin to your project. In Maven, just add the following dependencies to your pom.xml file:

        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-core</artifactId>
            <version>${serenity.version}</version>
        </dependency>
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-junit</artifactId>
            <version>${serenity.version}</version>
        </dependency>
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-cucumber</artifactId>
            <version>${serenity.cucumber.version}</version>
        </dependency>

The equivalent in Gradle is:

    testCompile 'net.serenity-bdd:core:2.0.64'
    testCompile 'net.serenity-bdd:serenity-junit:2.0.64'
    testCompile 'net.serenity-bdd:serenity-cucumber:2.0.19'

Working with Cucumber 4

The Cucumber 4 dependency setup is a bit more complicated, as you need to override the Cucumber 2 dependency in Serenity core. In Maven, you need add the following dependency configuration to your pom.xml file:

        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-core</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>io.cucumber</groupId>
                    <artifactId>cucumber-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-screenplay</artifactId>
            <version>${serenity.version}</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>io.cucumber</groupId>
                    <artifactId>cucumber-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-cucumber4</artifactId>
            <version>${serenity.cucumber4.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>4.2.0</version>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>4.2.0</version>
        </dependency>

The equivalent in Gradle is:

configurations.all {
    resolutionStrategy {
        force "io.cucumber:cucumber-core:4.2.0"
    }
}

dependencies {
    testCompile "net.serenity-bdd:serenity-core:2.0.64",
            "net.serenity-bdd:serenity-cucumber4:1.0.19",
            "io.cucumber:cucumber-core:4.2.0",
            "io.cucumber:cucumber-junit:4.2.0"

    // Other dependencies
    testCompile "org.slf4j:slf4j-api:1.7.5",
            "junit:junit:4.12",
            "org.assertj:assertj-core:1.7.0"
}

Using the Cucumber Maven Archetype

If you are starting from scratch, a Cucumber archetype for Cucumber 2 is available to help you jumpstart a new project. As usual, you can run mvn archetype:generate -Dfilter=serenity from the command line and then select the net.serenity-bdd:serenity-cucumber-archetype archetype from the proposed list of archetypes. Or you can use your favourite IDE to generate a new Maven project using an archetype.

Writing Executable Specifications with Cucumber and Serenity

In Cucumber, scenarios are stored in Feature Files, which contain an overall description of a feature as well as a number of scenarios. The Feature File for the first example we saw earlier is called cash_withdrawals.feature, and looks something like this:

Feature: Cash Withdrawals

  Scenario: Cash withdrawal
    Given Clive has $1000 in his Current account
    When he withdraws $100 in cash
    Then his remaining balance should be $900

Organising your feature files

The simplest way to organise your Serenity/Cucumber feature files is to place them in the src/test/resources/features directory.

But placing all your feature files in one directory can become messy as your project grows. To make the feature files easier to manage, in Serenity projects we typically organise the feature files in sub-directories that reflect the higher-level requirements. You can think of each subdirectory as a chapter or section heading in your living documentation, with the features grouped inside these sections. For example, in the following directory structure, we have feature definitions for several capabilities related to an online store, including 'search_for_products', 'make_a_purchase' and 'request_a_refund':

|____src
| |____test
| | |____resources
| | | |____features
| | | | |____banking_app
| | | | | |____deposits_and_withdrawals
| | | | | | |____internal_transfers.feature
| | | | | | |____inter_bank_transfers.feature
| | | | | | |____cash_withdrawals.feature
| | | | | |____opening_an_account
| | | | | | |____opening_current_savings.feature
| | | | | | |____opening_business_accounts.feature
| | | | | | |____opening_current_accounts.feature
| | | | | |____closing_an_account
| | | | | | |____closing_a_business_account.feature
| | | | | | |____closing_an_individual_account.feature

This requirements structure is reflected in the Requirements tab of the Serenity reports:

cucumber requirements hierarchy
Figure 2. A Cucumber requirements hierarchy report

The Scenario Runner

Cucumber runs the feature files via JUnit, and needs a dedicated test runner class to actually run the feature files. When you run the tests with Serenity, you use the CucumberWithSerenity test runner. You also need to use the @CucumberOptions class to provide the root directory where the feature files can be found.

A simple test runner looks like this:

@RunWith(CucumberWithSerenity.class)                    (1)
@CucumberOptions(
        features="src/test/resources/features",         (2)
        glue = "net.serenity_bdd.samples")              (3)
public class CashWithdrawals {
}
  1. Serenity Cucumber feature files need the CucumberWithSerenity test runner.

  2. The features attribute indicates where to look for the feature files, and can be a path to a directory or to a specific feature file

  3. The glue attribute tells Cucumber where to find the Step Definition classes

Cucumber Step definitions

In Cucumber, each line of the Gherkin scenario maps to a method in a Java class, known as a 'Step Definition'. These use annotations like @Given, @When and @Then match lines in the scenario to Java methods. In this section, we will briefly look at how to write your own Cucumber step definitions in both Cucumber 2 and Cucumber 4.

Writing Step Definitions in Cucumber 2

In Cucumber 2, you define simple regular expressions to indicate parameters that will be passed into the methods. The regular expressions indicate where you want to pass in a parameter. For example, in the following method, we use three regular expressions to match the name of the customer, the current account balance, and the account type:

@Given("^(.*) has \\$(\\d+) in his (.*) account$")
public void in_his_current_account(String customer, int balance, String accountType) {
}

Simple type conversions

As you can see in the previous code, Cucumber will convert the expressions you pass automatically into basic types such as String, int and float. You can also convert to enums, which can make your tests a bit more reliable. For example, we could create an enum to represent the different account types:

public enum AccountType {
    Current, Savings, Investment
}

We could then use this enum as a parameter for the step definition method: Cucumber will do the conversion automatically:

@Given("^(.*) has \\$(\\d+) in his (.*) account$")
public void in_his_current_account(String customer, int balance, AccountType accountType) {
}

Working with lists and tables

Lists and tables are often used to make scenarios more concise and readable. In the following step, we pass in a list of bank accounts:

Given Clive has the following accounts:
  | Account | Balance |
  | Current | 1000    |
  | Savings | 2000    |

There are a few ways we can pass this table into our step definition. One is to use the Cucumber DataTable class, which provides a number of helper methods to extract values from the table, or to convert the rows into a more usable form.

For example, suppose you had the following simple Java class to represent the account entries in the tables:

class AccountDetails {
    private final AccountType account;
    private final int balance;

    public AccountDetails(AccountType account, int balance) {
        this.account = account;
        this.balance = balance;
    }

    public AccountType getAccount() {
        return account;
    }

    public int getBalance() {
        return balance;
    }
}

You could pass the table into your step definition method as a DataTable, and then use the asList() method to convert the entries to a list of AccountDetails:

@Given("^Clive has the following accounts:$")
public void cliveHasTheFollowingAccounts(DataTable accounts) {
    List<AccountDetails> accountDetails = accounts.asList(AccountDetails.class);
    ...
}

Cucumber also allows you to bypass the DataTable class and pass in a list of domain objects. So the step definition method shown above could be simplified as follows:

@Given("^Clive has the following accounts:$")
public void cliveHasTheFollowingAccounts(List<AccountDetails> accountDetails) {
    ...
}

Another approach is to pass in a list of maps; each map represents one of the rows in the table, with the column headers as the keys:

@Given("^Clive has the following accounts:$")
public void cliveHasTheFollowingAccounts(List<Map<String,String>> accountDetails) {
}

This approach can be useful when you use a table to override default values. For example, an Account object might have many more fields than simply the account and balance fields, but in our example above we could safely use sensible default values for all the other fields, only overriding the values we are really interested in.

Writing Step Definitions in Cucumber 4

In Cucumber 4, you can use regular expressions, but also the more readable Cucumber Expressions notation.

Cucumber Expressions use tokens surrounded by curly brackets such as "{string}","{word}","{int}" and "{float}" to represent common types. {string} represents a sequence of characters enclosed in quotes (such as "red car"), whereas {word} matches a word containing no whitespaces and not enclosed in quotes (red, but not "red" or red green). The anonymous parameter type ("{}") can be used to match any sequence of characters, like the "(.*)" wildcard regular expression.

The following step definition shows how we could use Cucumber Expressions for the "Given Clive has $1000 in his Current account" step we saw earlier:

@Given("{word} has ${int} in his {word} account")
public void in_his_current_account(String customer, int balance, String accountType) {
}

Simple type conversions

Cucumber 4 does not automatically convert parameters to enums and domain classes the way Cucumber 2 does - a little more configuration is required. If you want to pass in an AccountType enum value directly, you need to define a custom parameter type.

Each custom parameter type has a name you can define, which makes the step definitions much more readable. For instance, we could define a custom parameter type called AccountType, and include it in the step definition annotation like this:

@Given("{word} has ${int} in his {AccountType} account")
public void in_his_current_account(String customer,
                                   int balance,
                                   AccountType accountType) {
}

However for this to work, we need to declare the AccountType custom parameter in the Cucumber type registry. We do this by adding an implementation of the TypeRegistryConfigurer class to our step definitions and defining a custom parameter type for our enum, as shown here:

public class TypeRegistryConfiguration implements TypeRegistryConfigurer {
    @Override
    public Locale locale() {
        return ENGLISH;
    }

    @Override
    public void configureTypeRegistry(TypeRegistry typeRegistry) {
        typeRegistry.defineParameterType(ParameterType.fromEnum(AccountType.class));
    }
}

Converting parameters to custom classes

We can also use the Cucumber type registry to convert parameters to domain objects. This helps reduce boiler-plate code in our step definitions and makes the Cucumber Expressions more readable.

One common use of this feature is in Screenplay tests, where we can define a custom {actor} parameter type. Normally, to convert the name of an actor to a Screenplay actor, we would use the Actor.named() method, like this:

@Given("{word} has ${int} in his {AccountType} account")
public void in_his_current_account(String customerName,
                                   int balance,
                                   AccountType accountType) {
    Actor customer = Actor.named(customerName);
    customer.attemptsTo(
        OpenABankAccount.ofType(accountType).withABalanceOf(balance)
    );
    ...
}

In Cucumber 4, we can define a custom parameter type so that we can pass a Screenplay actor directly into a step definition method, like this:

@Given("{actor} has ${int} in his {AccountType} account")
public void in_his_current_account(Actor customer,
                                   int balance,
                                   AccountType accountType) {
     customer.attemptsTo(
         OpenABankAccount.ofType(accountType).withABalanceOf(balance)
     );
}

To do this, we need to include the actor creation logic in the Cucumber Type Registry, by using the defineParameterType() method in the TypeRegistryConfiguration class:

public class TypeRegistryConfiguration implements TypeRegistryConfigurer {
    @Override
    public Locale locale() { return ENGLISH; }

    @Override
    public void configureTypeRegistry(TypeRegistry typeRegistry) {
      typeRegistry.defineParameterType(new ParameterType<>(
              "actor",                                        (1)
              ".*",                                           (2)
              Actor.class,                                    (3)
              (String actorName) -> Actor.named(actorName)))  (4)
        );
    }
}
  1. The name of the custom parameter

  2. The regular expression used to capture the parameter value

  3. The parameter type

  4. A lambda expression to convert the String parameter to the target type

Working with data tables

In Cucumber 2, it is relatively easy to pass a data table to a step definition method as a list of domain objects. We saw this earlier with the following step:

Given Clive has the following accounts:
  | Account | Balance |
  | Current | 1000    |
  | Savings | 2000    |

In Cucumber 4, as in Cucumber 2, we can pass in a DataTable or a list of maps:

@Given("^Clive has the following accounts:$")
public void hasTheFollowingAccounts(List<Map<String,String>> accounts) {
    ....
}

However if we want to pass in a list of domain objects, or convert a DataTable to a domain object or a list of domain objects, we need to do a little more work.

Converting Data Table rows to objects

Probably the simplest approach is to configure Cucumber to convert data table entries into Java objects using an ObjectMapper. Cucumber comes bundled with the Jackson ObjectMapper, which converts Cucumber table entries to JSON, and then into Java. This is relatively easy to set up, but it does need a few minor tweaks to your Gherkin and Java code.

First of all, Jackson needs a default constructor in the classes it creates. So we need to refactor the AccountDetails class to look like this:

public class AccountDetails {
    private AccountType account;
    private int balance;

    public AccountDetails(AccountType account, int balance) {
        this.account = account;
        this.balance = balance;
    }

    public AccountDetails() {} (1)

    public AccountType getAccount() {
        return account;
    }

    public int getBalance() {
        return balance;
    }
}
  1. Jackson needs a default constructor to work correctly

Secondly, the column names need to match the Java field names exactly. One option is to write the column headings in lower case, like this:

Given Clive has the following accounts:
  | account | balance |
  | Current | 1000    |
  | Savings | 2000    |

The other option is to use the @JsonProperty annotation to change the default field names used by Jackson:

public class AccountDetails {
    private AccountType account;
    private int balance;

    public AccountDetails(AccountType account, int balance) {
        this.account = account;
        this.balance = balance;
    }

    public AccountDetails() {}

    @JsonProperty("Account")
    public AccountType getAccount() {
        return account;
    }

    @JsonProperty("Balance")
    public int getBalance() {
        return balance;
    }
}

The last thing we need to do is to register the ObjectMapper with the Cucumber type registry. This involves writing a Transformer class that implements the TableEntryByTypeTransformer interface. One that uses the bundled Jackson ObjectMapper class looks like this:

class Transformer implements TableEntryByTypeTransformer {

    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public <T> T transform(Map<String, String> map,
                          Class<T> aClass,
                          TableCellByTypeTransformer typeTransformer) {
        return objectMapper.convertValue(map, aClass);
    }
}

Next, we need to register the transformer by adding the following code to the configureTypeRegistry() method we saw earlier:

Transformer transformer = new Transformer();
typeRegistry.setDefaultDataTableEntryTransformer(transformer);

We can now pass the list of account details into the step definition method as a list of AccountDetail objects:

@Given("Clive has the following accounts:")
public void cliveHasTheFollowingAccounts(List<AccountDetails> accounts) {
}

Using Serenity step libraries in Cucumber Step definitions

It is perfectly possible to call classic test automation code directly in your step definition methods. In this case, Serenity will only report on the Given..When..Then steps, and leave you full control of the underlying code.

Many teams however find it useful to organise their tests into reusable components and domain layer classes. And Serenity provides many ways to help with this.

Serenity Step Libraries integrate smoothly into Cucumber Step Definition files; all you need to do is to annotate a step library variable with the @Steps annotation. For example, suppose our banking application has a set of web services to handle account balances and withdrawals. We could wrap these web service calls in Serenity Action classes, and inject these action classes into the step definition classes using the Serenity @Steps annotation, e.g.

public class CashWithdrawalsStepDefinitions {
    @Steps
    CreateAccountAction createAnAccount;

    @Given("{word} has ${int} in his {word} account")
    public void in_his_current_account(String customer,
                                       int balance,
                                       String accountType) {
        createAnAccount.forCustomer(customer)
                       .ofType(accountType)
                       .withABalanceOf(balance);
    }
    ...
}

The Action class uses a builder pattern for better readability. We use the @Step annotation on the last method to ensure that we get a complete description of the step in our reports:

public class CreateAccountAction {
    String customerName;
    String accountType;

    public CreateAccountAction forCustomer(String customerName) {
        this.customerName = customerName;
        return this;
    }

    public CreateAccountAction ofType(String accountType) {
        this.accountType = accountType;
        return this;
    }

    @Step("Create a #accountType account for customer #customerName with a balance of ${0}")
    public int withABalanceOf(int balance) {
        // Call the appropriate web service and return the account unique ID
    }
}

The other step definition methods would use similar step libraries. Some teams group their step libraries by theme or end point (for example, they might place all the web service calls related to managing a bank account in a single BankAccountActions step library). Others prefer to enforce a stricter separation of concerns, and have a single class per action (this approach is know as the Action Classes pattern), as illustrated here:

public class CashWithdrawalsStepDefinitions {

    @Steps
    CreateAccountAction createAnAccount;

    @Steps
    WithdrawAction withdraw;

    @Steps
    AccountBalanceAction accountBalance;

    int accountIdentifier;

    @Given("{word} has ${int} in his {word} account")
    public void in_his_current_account(String customer, int balance, String accountType) {
        accountIdentifier = createAnAccount.forCustomer(customer).ofType(accountType).withABalanceOf(balance);
    }

    @When("he withdraws ${int} in cash")
    public void he_withdraws_in_cash(int withdrawal) {
        withdraw.fromAccount(accountIdentifier).theSumOf(withdrawal);
    }

    @Then("his remaining balance should be ${int}")
    public void his_remaining_balance_should_be(int expectedBalance) {
        assertThat(accountBalance.forAccount(accountIdentifier)).isEqualTo(expectedBalance);
    }
}

Integrating with Serenity Page Objects

Integration Cucumber with Serenity UI tests is also easy. Unlike with JUnit, you don’t need to declare an @Managed WebDriver instance in your tests; Serenity will automatically instantiate a driver on demand.

For example, the following scenario is a very simple example of using a search engine:

Feature: Search by keyword

  Scenario: Search for terms by a single keyword
    Given Serge is on the Search page
    When he searches by "Tomato"
    Then he should only see search results containing the word "Tomato"

We could use Serenity Page Objects or UI Action Classes to model the UI interactions. For example, the following Serenity PageObject models the DuckDuckGo home page:

@DefaultUrl("https://duckduckgo.com")
public class SearchPage extends PageObject {

    public void searchBy(String keyword) {
        $("#search_form_input_homepage").sendKeys(keyword, Keys.ENTER);
    }
}

And this one allows us to query the search results:

public class ResultsPage extends PageObject {

    public List<String> getSearchResultTitles() {
        return findAll(".results .result__title")
                .stream()
                .map(WebElementFacade::getTextContent)
                .collect(Collectors.toList());
    }
}

We could use both of these page objects directly in our Cucumber step definition code, as shown here:

public class SearchStepDefinitions {

    SearchPage searchPage;
    ResultsPage resultsPage;

    @Given("Serge is on the Search page")
    public void serge_is_on_the_Search_page() {
        searchPage.open();
    }

    @When("he searches by {string}")
    public void he_searches_by(String keyword) {
        searchPage.searchBy(keyword);
    }

    @Then("he should only see search results containing the word {string}")
    public void he_should_only_see_search_results_containing_the_word(String expectedKeyword) {
        resultsPage.getSearchResultTitles().forEach(
                title -> assertThat(title).containsIgnoringCase(expectedKeyword)
        );
    }
}

Alternatively, we could use Action Classes or Step Libraries to encapsulate the UI logic. In both cases, Serenity will instantiate the WebDriver instance for us the first time a page object is invoked.

Configuring Cucumber and Serenity

Serenity provides a number of configuration options when using Cucumber.

Maintaining the browser open for the entire feature

Reopening a browser session for each scenario can slow down your tests. If you want Serenity to keep the browser open for the duration of a feature, you can set the following property in your serenity.properties or serenity.conf file:

serenity.restart.for.each = feature

Maintaining state between scenarios

You may also wish to maintain state between scenarios. By default, Serenity will use new step libraries and action classes for each scenario. However, if you want to share data between the scenarios of a feature, you can use the following option:

serenity.maintain.session = true

With this value set, you can now use either the @Shared annotation, or the @Steps(shared=true). For example, suppose that the CreateAccountAction needs to retrieve a security token before making any calls, but that it only needs to do this once. We could tell Serenity to maintain the same CreateAccountAction step library for all the scenarios in a feature file like this:

@Shared
CreateAccountAction createAnAccount;

Integrating Cucumber with Serenity Screenplay

The Screenplay Pattern is a popular technique among experienced test automation practitioners that makes it easier to write cleaner, more maintainable test automation code at scale.

Defining the Screenplay cast

When we use Screenplay with Cucumber, it is possible to define an actor programmatically as we do in JUnit tests. However it is more common to pass the name of the actor dynamically to the step definition methods. We do this using the OnStage class.

In Cucumber, Serenity needs a cast of actors to draw on in the various step definitions. Each actor is uniquely identified by their name. We set this up by calling the setTheStage() method in a @Before-annotated method:

@Before
public void prepareTests() {
    OnState.setTheStage(new OnlineCast());
}

The OnlineCast is a cast of actors with the ability to browse the web. For more sophisticated actor setups, we can use the Cast.whereEveryoneCan() method:

@Before
public void prepareTests() {
    OnState.setTheStage(OnlineCast.whereEveryoneCan(CallAnApi.at(ADMIN_REST_URL)));
}

Invoking an actor

Once we have set up the cast, any step can refer to an actor by name. We can do this using the OnStage.theActorCalled() method, as shown below:

@Given("{word} has ${int} in his {AccountType} account")
public void in_his_current_account(String customerName,
                                   int balance,
                                   AccountType accountType) {
    OnStage.theActorCalled(customerName).attemptsTo(
        OpenABankAccount.ofType(accountType).withABalanceOf(balance)
    );
    ...
}

We often use a static import to simplify this code:

static import net.serenitybdd.screenplay.actors.OnStage.theActorCalled;

@Given("{word} has ${int} in his {AccountType} account")
public void in_his_current_account(String customerName,
                                   int balance,
                                   AccountType accountType) {
    theActorCalled(customerName).attemptsTo(
        OpenABankAccount.ofType(accountType).withABalanceOf(balance)
    );
    ...
}

If we do not have the name of the actor available within a step definition, we can also use the theActorInTheSpotlight() method, e.g.

@When("s?he filters her list to show only (.*) tasks")
public void filters_tasks_by(TodoStatusFilter status) {
    OnStage.theActorInTheSpotlight().attemptsTo(FilterItems.toShow(status));
}

A more short-hand way of writing this code is to use the OnStage.withCurrentActor() method:

@When("s?he filters her list to show only (.*) tasks")
public void filters_tasks_by(TodoStatusFilter status) {
  withCurrentActor(
          FilterItems.toShow(status)
  );
}

Using Actors in Hook methods

If you need to perform UI actions in test setup (for example, to log on to an application), you may not know the name of the actor who will be first mentioned in the Gherkin steps. You can still use an actor in this case, but you need to use the OnStage.aNewActor() method.

@Before(order=1)
public void set_the_stage() {
    OnStage.setTheStage(new OnlineCast());
}

@Before(order=2)
public void openApplication() {
    OnStage.aNewActor().attemptsTo(
        OpenTheTodoMVCApplication.onTheHomePage()
    );
}

The aNewActor() method will create a new, unnamed actor, who will be associated with the first actor referred to by name in the subsequent steps.