Cucumber Step Definitions and Expressions

A step definition is the Java method that runs when Cucumber reaches a Gherkin step. The feature file says what should happen in business language; the step definition does it — usually by calling page objects. This guide covers how steps are matched, Cucumber Expressions in detail (every match shown was tested with the official library), passing tables and text blocks, and the undefined and ambiguous step errors you'll meet.

Analogy: the feature file is a recipe ("add sugar"); the step definition is the cook actually doing it.

Advertisement

From Feature File to Code

Feature: Money transfer

  Background:
    Given the user is logged in as "asha"

  Scenario: Transfer between own accounts
    When the user transfers 500.00 from "Savings" to "Current"
    Then the "Savings" balance should be 1500.00
    And the transfer history shows 1 entry
public class TransferSteps {
    private final TestContext context;
    public TransferSteps(TestContext context) { this.context = context; }   // shared state via DI

    @Given("the user is logged in as {string}")
    public void loggedIn(String user) {
        new LoginPage(context.driver()).loginAs(user, System.getenv("APP_PASSWORD"));
    }

    @When("the user transfers {double} from {string} to {string}")
    public void transfer(double amount, String from, String to) {
        new TransferPage(context.driver()).transfer(amount, from, to);
    }

    @Then("the {string} balance should be {double}")
    public void balance(String account, double expected) {
        Assert.assertEquals(new AccountsPage(context.driver()).balance(account), expected, 0.001);
    }

    @Then("the transfer history shows {int} entr(y)(ies)")
    public void history(int count) {
        Assert.assertEquals(new HistoryPage(context.driver()).entries(), count);
    }
}

Cucumber scans the glue packages named in the runner, finds the method whose expression matches each step's text, converts the captured values to the parameter types, and calls it. Step definitions should stay thin — call page objects, don't write Selenium code here. Sharing the driver between step classes: BDD Hybrid Framework.

Given, When, Then — and And, But

Keyword Purpose Example
Given Starting state (context) the user is logged in as "asha"
When The action or event the user transfers 500.00 …
Then The expected outcome the balance should be 1500.00
And / But Continue the previous kind of step And the transfer history shows 1 entry

Cucumber matches on the step text, not the keyword. And the transfer history shows 1 entry runs the method annotated @Then("the transfer history shows …") — there's no @And needed, and a step annotated @Given would even match if written with When. The keyword is for readers; use the one that fits the meaning.

Cucumber Expressions ⭐

Cucumber Expressions are the readable default for step patterns — placeholders instead of regular expressions. Every result below was checked with the official cucumber-expressions library:

Expression Step text Captured
… username {string} and password {string} … username "asha" and password "s3cret" "asha", "s3cret"
the user clicks on {string} the user clicks on 'Login' "Login" — single quotes work too
the cart has {int} item(s) the cart has 1 item / has 3 items 1 / 3
the total is {float} the total is 49.99 49.99
the user selects {word} the user selects Pune "Pune"
the user selects {word} the user selects New Delhi ❌ no match — {word} stops at a space
I have {int} cucumber(s) in my belly/stomach I have 5 cucumbers in my stomach 5 — optional text and alternatives
the price is {} the price is anything here "anything here" — anonymous, matches anything
the cart has {int} items the cart has three items ❌ no match — "three" isn't an integer

Built-in parameter types

{int}, {long}, {byte}, {short}, {float}, {double}, {biginteger}, {bigdecimal}, {word} (one word, no spaces), {string} (text in single or double quotes, quotes removed) and {} (anything).

Optional and alternative text

  • item(s) — the text in brackets is optional, so one step handles singular and plural.
  • belly/stomach — either word matches (no spaces around the slash).

Custom parameter types

public class ParameterTypes {
    @ParameterType("admin|editor|viewer")
    public Role role(String name) {
        return Role.valueOf(name.toUpperCase());
    }
}

@Given("a user with role {role}")
public void userWithRole(Role role) { … }      // "a user with role editor" → Role.EDITOR

Custom types turn step text straight into domain objects (enums, dates, money) and reject invalid values at matching time — a step with "role manager" simply won't match.

Cucumber Expressions vs Regular Expressions

// Regular expression — anchors (^ $) tell Cucumber this is a regex
@When("^user logs in with username \"([^\"]*)\" and password \"([^\"]*)\"$")
public void login(String username, String password) { … }

// Cucumber Expression — same step, readable
@When("user logs in with username {string} and password {string}")
public void login(String username, String password) { … }
  Cucumber Expressions Regular expressions
Readability High — plain words and placeholders Lower — escapes and capture groups
Type conversion Automatic ({int} → int) By method parameter type
Flexibility Optional/alternative text, custom types Any pattern
Default Yes (Cucumber 3+) Used when the pattern starts with ^ or ends with $

Use Cucumber Expressions by default; fall back to regex only for patterns they can't express.

Passing Tables and Text Blocks

  Scenario: Add payees
    When the user adds these payees:
      | name  | account  | bank |
      | Ravi  | 12345678 | HDFC |
      | Meera | 87654321 | SBI  |
    Then 2 payees are listed

  Scenario: Transfer note
    When the user adds the note:
      """
      Rent for October
      Flat 4B
      """
    Then the note is saved
@When("the user adds these payees:")
public void addPayees(DataTable table) {
    for (Map<String, String> payee : table.asMaps()) {      // one map per row, header as keys
        payeePage.add(payee.get("name"), payee.get("account"), payee.get("bank"));
    }
}

@When("the user adds the note:")
public void addNote(String note) { … }                        // a DocString arrives as one String

A DataTable passes several rows to one step; a Scenario Outline runs the whole scenario once per Examples row. More: Feature Files, Scenario Outline & Background.

Undefined Steps

If no step definition matches, Cucumber marks the step undefined, skips the rest of the scenario, and prints a ready-made snippet:

@Then("the user sees the account summary")
public void the_user_sees_the_account_summary() {
    // Write code here that turns the phrase above into concrete actions
    throw new io.cucumber.java.PendingException();
}

Usual causes: the step isn't implemented yet, a typo or extra word in the feature file, or the step class isn't in the runner's glue packages. Set @CucumberOptions(dryRun = true) to list undefined steps without running the browser, and strict behaviour (the default in Cucumber 7) fails the run on undefined steps.

Ambiguous Steps

If two step definitions match the same text, Cucumber throws AmbiguousStepDefinitionsException naming both. Typical causes: the same step copied into two classes, or overlapping expressions such as the user clicks {string} and the user clicks {}. Fix it by keeping one definition in a shared steps class and making expressions specific. Full guide: Ambiguous & Undefined Step Fix.

Common Steps: Background vs Hooks

  • Background — business-visible steps shared by every scenario in one feature file ("Given the user is logged in").
  • Hooks (@Before, @After) — technical setup and teardown readers don't need to see (open the browser, take a screenshot on failure, close it).

Watch every step of a scenario being matched and executed in the Cucumber BDD Visualizer.

From Real Projects

BDD and TDD are part of the frameworks I've worked with. What makes BDD valuable is the conversation: scenarios written in plain language that testers, developers and product people all understand, which is also the idea behind Testsigma's plain-English test creation. Keep each scenario focused on one behaviour and let the step definitions call your existing page and business-library code. Keep step definitions short and reusable across scenarios.

📚 Official documentation: Cucumber documentation

FAQs

What is a step definition?

A Java method annotated with @Given, @When or @Then whose expression matches a Gherkin step; Cucumber runs it when it reaches that step.

It matches each step's text against the expressions in the glue packages, converts captured values to the method's parameter types, and calls the method.

Do I need an @And annotation?

No — matching uses the step text only. And/But steps run whichever definition matches their text.

What's the difference between {word} and {string}?

{word} matches one word without spaces or quotes; {string} matches quoted text (single or double quotes) and can contain spaces.

What happens if a step definition is missing?

The step is undefined, the scenario doesn't pass, and Cucumber prints a snippet to implement.

What happens if two step definitions match?

Cucumber throws AmbiguousStepDefinitionsException. Remove the duplicate or make the expressions more specific.