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.
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.
How does Cucumber link steps to code?
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.