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.

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
1.What is BDD?
2.What is Cucumber?
3.What is Gherkin?
4.Is BDD worth it in 2026?
5.What's the difference between Cucumber and SpecFlow?
6.What's the difference between BDD and TDD?
7.How long does it take to learn BDD?
8.Can BDD replace unit tests?
9.Where can I practice BDD?
10.Is Cucumber still relevant in 2026?
Practice these questions
Rehearse Selenium and Playwright automation questions covering framework design, waits, locators and CI/CD.
Was this article helpful?
More from BDD Cucumber
Gherkin, step definitions, Cucumber with Java/JS.
- Automation TestingSpecFlow C# BDD Tutorial: Complete 2026 Guide
- AI in TestingChatGPT Gherkin Scenarios in 2026: The Complete BDD Playbook (Prompts, Anti-Patterns & FAQ)
- AI in TestingClaude for QA in 2026: The Complete Playbook (Test Plans, Cases, Bugs, Gherkin, PR Reviews & FAQ)
Keep building your QA edge
Pillar guides- Automation QA Engineer RoleSoftwareTestPilot's Automation QA role pageAutomation QA Engineer job scope, tools, salary, and hiring pipeline.
- QA Skills Hubstructured learning tracks for testersStructured skill tracks — Selenium, Playwright, Cypress, API, JMeter, SQL, Java, Python for testers.
- QA & Testing GlossaryQA terminology explained in plain English500+ software testing terms defined — from ISTQB vocabulary to CI/CD, AI testing, and framework jargon.
Continue reading

Playwright Locator Best Practices (2026) — The Only Guide You Need
11 min read
How to Migrate a Postman Collection to Playwright API Tests (2026 Guide)
12 min read
Why Every QA Engineer Must Master CI/CD Pipelines in 2026 (Or Risk Obsolescence)
12 min readRelated concepts, tools & standards around Automation Testing
A quick reference of the people, companies, frameworks and technologies most often mentioned alongside Automation Testing in real QA teams — useful when you're mapping a learning path, preparing for interviews, or scoping a new project.
Join the QA Community
Connect with fellow testers, share job leads, and get career advice.
Discussion
Ask a question, share your experience, or correct us. Be kind — real people are reading.