BDD Testing with Cucumber: Beginner Guide (2026)
The 2026 BDD testing with Cucumber beginner's guide — Gherkin syntax, step definitions in Java/JS/Python, scenario outlines, data tables, tags, hooks, and Cucumber + Selenium / Playwright integration.

In this article
- 1. What Is BDD in 2026?
- 2. Why Cucumber?
- 3. The BDD Workflow (the Three Amigos)
- 4. Gherkin Syntax Deep Dive
- 5. Install Cucumber Step by Step
- 6. Your First Feature File and Step Definitions
- 7. Scenario Outlines and Data Tables
- 8. Tags and Hooks
- 9. Cucumber + Selenium Integration
- 10. Cucumber + Playwright Integration
- 11. Reporting with Cucumber
- 12. BDD Best Practices for 2026
- 13. Author Bio & Next Steps
- Frequently asked questions
What you'll master: BDD philosophy, production-quality Gherkin feature files, step definitions in Java/JS/Python, Cucumber + Selenium and Cucumber + Playwright integration, CI execution, and the common mistakes that turn BDD projects into maintenance nightmares.
1. What Is BDD in 2026?
Behavior-Driven Development (BDD) is a software development methodology that emerged from Test-Driven Development in the mid-2000s. Dan North coined the term in 2006 to address a critical gap: TDD was excellent at the unit level but failed to align developers, testers and business stakeholders on what to build.
BDD's core idea: write executable specifications in plain language that everyone — developer, QA, PM, business analyst — can read, write and review. The format: Given (preconditions) When (action) Then (expected outcome). This pattern, called Gherkin, is the lingua franca of BDD.
BDD has matured from a niche Agile practice into a mainstream methodology, especially in:
- Banking and fintech — where regulatory compliance demands documented acceptance criteria
- Healthcare and life sciences — where traceability between requirements and tests is mandatory
- E-commerce — where business-critical flows need shared understanding across teams
- Government and defense — where stakeholder signoff requires executable specifications
The default tool remains Cucumber. Alternatives: SpecFlow (.NET), Behave (Python), JBehave (Java), CodeceptJS (JS), Gauge (multi-language).
🚀 Rehearse BDD interview rounds live with our AI mock interview and pair this guide with SpecFlow interview questions.
2. Why Cucumber?
| Dimension | Cucumber | In-house framework |
|---|---|---|
| Stakeholder readability | Excellent (Gherkin) | Poor (code) |
| Tooling ecosystem | Mature (plugins, reporters) | Custom |
| Multi-language | Java, JS, Ruby, Python | Single |
| Maintenance cost | Medium | High (custom) |
| Onboarding speed | Fast — Gherkin learnable in a day | Slow |
| Reports | HTML, JSON, JUnit, Allure | Custom |
| Community | Large | None |
For most teams starting BDD, Cucumber is the right choice. Pick SpecFlow if you're in a .NET-only shop; pick Behave if Python is your primary language. For code-first comparison, see our Selenium WebDriver guide.
3. The BDD Workflow (the Three Amigos)
BDD works because it requires three roles to collaborate on every feature.
The Three Amigos
| Role | Brings |
|---|---|
| Business / PM | What the feature should do, what value it creates |
| Developer | How to build it, technical constraints, refactoring approach |
| Tester / QA | Edge cases, validation, exploratory scenarios |
The discovery workshop (the most under-used step)
Before writing any Gherkin, the Three Amigos hold a discovery workshop (30–60 min per story). They discuss the user story, identify concrete examples of behavior, surface edge cases early, and agree on scope. The output: a set of Gherkin scenarios the team has collectively agreed on. By the time code is written, there is no ambiguity.
BDD in the sprint
Day 1: Three Amigos discovery workshop → Gherkin scenarios agreed
Day 2: QA writes step definitions (or pairs with dev)
Day 3: Developer implements feature, runs scenarios
Day 4: Scenarios pass → mark story done
Day 5: Regression run → verify no breakageFor broader Agile QA context, see our manual testing guide.
4. Gherkin Syntax Deep Dive
Feature file structure
Feature: User login
As a registered user
I want to log in to the application
So that I can access my account
Background:
Given the application is running at "https://staging.example.com"
Scenario: Successful login
Given I am on the login page
When I enter "admin@example.com" as the email
And I enter "Sup3rSecret!" as the password
And I click the "Sign in" button
Then I should be on the dashboard page
And I should see "Welcome, admin"
Scenario: Invalid password
Given I am on the login page
When I enter "admin@example.com" as the email
And I enter "wrong-password" as the password
And I click the "Sign in" button
Then I should see an error "Invalid credentials"Keywords
| Keyword | Purpose |
|---|---|
Feature | Top-level description of the feature |
Rule | Groups scenarios under a business rule (Gherkin 6+) |
Background | Steps that run before every scenario in the feature |
Scenario | A single concrete example of behavior |
Scenario Outline | A template scenario with multiple data sets |
Given | Preconditions (state before the action) |
When | The action (the event being tested) |
Then | The expected outcome (the verification) |
And / But | Continuation of Given/When/Then |
@tag | Marker for filtering or grouping scenarios |
The Given-When-Then rule
- Given — state before the test (past tense: "I am logged in")
- When — the action (present tense: "I click submit")
- Then — the outcome (assertions: "I should see…")
Common mistake: writing the assertion in When. Move it to Then.
5. Install Cucumber Step by Step
Java + Maven
<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.18.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>7.18.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.3</version>
<scope>test</scope>
</dependency>
</dependencies>Create src/test/resources/features/ for .feature files and src/test/java/com/qa/cucumber/ for step definitions.
JavaScript + npm
npm install --save-dev @cucumber/cucumberCreate features/ for .feature files and features/step-definitions/ for JS steps.
Python + pip
pip install behaveCreate features/ for .feature files and features/steps/ for Python step files.
6. Your First Feature File and Step Definitions
Java
Feature file: src/test/resources/features/login.feature
Feature: User login
Scenario: Successful login
Given I am on the login page
When I enter "admin@example.com" as the email
And I enter "Sup3rSecret!" as the password
And I click the "Sign in" button
Then I should be on the dashboard page
And I should see "Welcome, admin"Step definitions: LoginSteps.java
package com.qa.cucumber;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.cucumber.java.en.Then;
import static org.junit.jupiter.api.Assertions.*;
public class LoginSteps {
@Given("I am on the login page")
public void iAmOnTheLoginPage() {
DriverFactory.getDriver().get("https://example.com/login");
}
@When("I enter {string} as the email")
public void iEnterEmail(String email) {
DriverFactory.getDriver().findElement(org.openqa.selenium.By.id("email")).sendKeys(email);
}
@When("I enter {string} as the password")
public void iEnterPassword(String password) {
DriverFactory.getDriver().findElement(org.openqa.selenium.By.id("password")).sendKeys(password);
}
@When("I click the {string} button")
public void iClickTheButton(String label) {
DriverFactory.getDriver().findElement(org.openqa.selenium.By.cssSelector("button")).click();
}
@Then("I should be on the dashboard page")
public void iShouldBeOnTheDashboardPage() {
String url = DriverFactory.getDriver().getCurrentUrl();
assertTrue(url.contains("/dashboard"), "Expected /dashboard, got " + url);
}
@Then("I should see {string}")
public void iShouldSee(String text) {
assertTrue(DriverFactory.getDriver().getPageSource().contains(text));
}
}Test runner: RunCucumberTest.java
@RunWith(Cucumber.class)
@CucumberOptions(
features = "src/test/resources/features",
glue = "com.qa.cucumber",
plugin = {"pretty", "html:target/cucumber-report.html"}
)
public class RunCucumberTest {}Run with mvn test.
JavaScript (with Playwright)
const { Given, When, Then } = require('@cucumber/cucumber');
const { expect } = require('@playwright/test');
Given('I am on the login page', async function () {
await this.page.goto('https://example.com/login');
});
When('I enter {string} as the email', async function (email) {
await this.page.getByTestId('email').fill(email);
});
When('I enter {string} as the password', async function (password) {
await this.page.getByTestId('password').fill(password);
});
When('I click the {string} button', async function () {
await this.page.getByTestId('submit').click();
});
Then('I should be on the dashboard page', async function () {
await expect(this.page).toHaveURL(/\/dashboard$/);
});
Then('I should see {string}', async function (text) {
await expect(this.page.getByRole('heading')).toContainText(text);
});7. Scenario Outlines and Data Tables
Scenario Outline (the killer feature)
Run the same scenario against many data sets:
Scenario Outline: Login with multiple users
Given I am on the login page
When I enter "<email>" as the email
And I enter "<password>" as the password
And I click the "Sign in" button
Then I should see "<greeting>"
Examples:
| email | password | greeting |
| admin@example.com | Sup3rSecret! | Welcome, admin |
| viewer@example.com | ViewerPass1! | Welcome, viewer |
| guest@example.com | GuestPass1! | Welcome, guest |Cucumber runs this scenario once per row — 3 rows above = 3 test runs.
Data Tables
Scenario: Apply multiple promo codes
Given I am on the cart page
When I apply the following promo codes:
| code | expected_discount |
| WELCOME10 | 10% |
| SAVE20 | 20% |
| VIP30 | 30% |
Then I should see the total discount applied@When("I apply the following promo codes:")
public void iApplyTheFollowingPromoCodes(DataTable dataTable) {
List<Map<String,String>> codes = dataTable.asMaps(String.class, String.class);
for (Map<String,String> code : codes) {
// Apply code.get("code") and assert code.get("expected_discount")
}
}9. Cucumber + Selenium Integration
The most common stack: Cucumber + Selenium 4 + Java + JUnit 5 + Maven.
pom.xml
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.18.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.25.0</version>
</dependency>Driver factory
public class DriverFactory {
private static WebDriver driver;
public static WebDriver getDriver() {
if (driver == null) {
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}
return driver;
}
public static void quitDriver() {
if (driver != null) { driver.quit(); driver = null; }
}
}Page object
public class LoginPage {
private WebDriver driver;
public LoginPage(WebDriver driver) { this.driver = driver; }
public void navigate() { driver.get("https://example.com/login"); }
public LoginPage enterEmail(String e) { driver.findElement(By.id("email")).sendKeys(e); return this; }
public LoginPage enterPassword(String p) { driver.findElement(By.id("password")).sendKeys(p); return this; }
public DashboardPage clickSignIn() { driver.findElement(By.id("submit")).click(); return new DashboardPage(driver); }
}For the full Selenium POM pattern, see our Selenium WebDriver guide and Java for Selenium deep-dive.
10. Cucumber + Playwright Integration
For JS/TS teams, Cucumber + Playwright is a faster, more modern combination than Cucumber + Selenium.
npm install --save-dev @cucumber/cucumber @playwright/testconst { Before, BeforeAll, After, AfterAll } = require('@cucumber/cucumber');
const { chromium } = require('@playwright/test');
let browser;
let context;
BeforeAll(async function () {
browser = await chromium.launch({ headless: true });
});
Before(async function () {
context = await browser.newContext();
this.page = await context.newPage();
});
After(async function () { await context.close(); });
AfterAll(async function () { await browser.close(); });For the full Playwright setup, see our Playwright complete guide.
11. Reporting with Cucumber
Built-in HTML report
@CucumberOptions(plugin = {"pretty", "html:target/cucumber-report.html"})Open target/cucumber-report.html after a run.
JSON report for CI
@CucumberOptions(plugin = {"json:target/cucumber.json"})Cucumber Reports service
The Cucumber Reports service provides cross-browser trend analysis, flaky test detection, scenario search and historical reporting.
Allure + Cucumber
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-cucumber7-jvm</artifactId>
<version>2.27.0</version>
</dependency>mvn test
allure serve target/allure-resultsAllure's HTML report shows step-by-step screenshots, logs and timing for every scenario.
12. BDD Best Practices for 2026
Do
- Hold a Three Amigos discovery workshop before writing any Gherkin
- Write declarative scenarios, not imperative scripts
- Keep scenarios short (3–7 steps each)
- Use Scenario Outlines for data-driven scenarios
- Use tags to organize by feature area, priority and test type
- Use Background for shared preconditions
- Generate living documentation from Gherkin (publish the HTML report)
- Pair BDD with POM for clean code architecture
- Use Allure + Cucumber for the richest reports
Don't
- Don't write scenarios as imperative test scripts — use declarative language
- Don't write too many scenarios — 10–30 per feature is the sweet spot
- Don't put business logic in step definitions — they should be thin glue
- Don't write technical jargon in Gherkin (locators, API endpoints)
- Don't skip the discovery workshop — it's the most valuable part of BDD
- Don't use BDD for unit tests — it's an acceptance testing methodology
- Don't maintain BDD suites that no one reads
Frequently asked questions
What is BDD?
Behavior-Driven Development (BDD) is a methodology that uses executable specifications in plain language (Given-When-Then) to align developers, testers and business stakeholders on what to build. The most common BDD framework is Cucumber.
What is Cucumber?
Cucumber is an open-source tool that executes Gherkin specifications as automated tests. It supports Java, JavaScript, Ruby, Python and .NET (via SpecFlow).
What is Gherkin?
Gherkin is the plain-text language used to write BDD scenarios. It uses the keywords Feature, Scenario, Given, When, Then, And, But. The syntax is identical across all Cucumber implementations.
Is BDD worth it in 2026?
Yes — for teams that need shared understanding between business and engineering. The cost of writing BDD is 20–30% higher than code-only tests; the benefit is reduced rework and clearer requirements.
What's the difference between Cucumber and SpecFlow?
Cucumber is the open-source BDD framework. SpecFlow is the .NET port of Cucumber. Both use the same Gherkin syntax. Choose SpecFlow if you're in a .NET-only shop.
What's the difference between BDD and TDD?
TDD writes unit tests before code. BDD writes acceptance tests in plain language before code. They are complementary — use TDD at the unit level and BDD at the acceptance level.
How long does it take to learn BDD?
For an experienced QA engineer: 2–4 weeks to productive, including the Three Amigos workshop process. For a developer new to QA: 4–8 weeks.
Can BDD replace unit tests?
No. BDD is an acceptance testing methodology. Use unit tests for internal logic and BDD for externally-visible behavior that matters to business.
Where can I practice BDD?
Pick any web app. Write 5 scenarios for the login flow and 5 for the checkout flow. Run them with Cucumber + Selenium or Cucumber + Playwright.
Is Cucumber still relevant in 2026?
Yes — Cucumber 7.x is the current version. Alternatives like Gauge and CodeceptJS are gaining ground but Cucumber remains the default for BDD in enterprise.
Practice these questions
Rehearse Selenium and Playwright automation questions covering framework design, waits, locators and CI/CD.
Was this article helpful?
Keep building your QA edge
Pillar guidesContinue reading

Why Every QA Engineer Must Master CI/CD Pipelines in 2026 (Or Risk Obsolescence)
12 min read
Is Cypress Dead? Analyzing 2026 Playwright Market Share
12 min read
Why Tests Pass Locally But Fail in CI/CD (And the 6 Fixes That Actually Work in 2026)
13 min readJoin the QA Community
Connect with fellow testers, share job leads, and get career advice.
Stop Reinventing the Wheel. Upgrade Your QA Arsenal.
Take your testing skills from beginner to Lead Engineer. Supercharge your daily workflow with our premium digital resources.
- ⚡ Ready-to-use testing strategy templates
- 🔥 Advanced API & UI automation guides
- ⏱️ Save 10+ hours a week on test planning