SoftwareTestPilot
Automation TestingPublished: Updated: · 5 days ago28 min read

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.

Avinash Kamble
Avinash Kamble
Founder & QA Engineer at SoftwareTestPilot
Reviewed by Priyanka G.
Share:XLinkedInWhatsApp
BDD Testing with Cucumber Beginner Guide — 3D book cover with Gherkin code and Cucumber logo
BDD Testing with Cucumber Beginner Guide — 3D book cover with Gherkin code and Cucumber logo
In this article
  1. 1. What Is BDD in 2026?
  2. 2. Why Cucumber?
  3. 3. The BDD Workflow (the Three Amigos)
  4. 4. Gherkin Syntax Deep Dive
  5. 5. Install Cucumber Step by Step
  6. 6. Your First Feature File and Step Definitions
  7. 7. Scenario Outlines and Data Tables
  8. 8. Tags and Hooks
  9. 9. Cucumber + Selenium Integration
  10. 10. Cucumber + Playwright Integration
  11. 11. Reporting with Cucumber
  12. 12. BDD Best Practices for 2026
  13. 13. Author Bio & Next Steps
  14. 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?

DimensionCucumberIn-house framework
Stakeholder readabilityExcellent (Gherkin)Poor (code)
Tooling ecosystemMature (plugins, reporters)Custom
Multi-languageJava, JS, Ruby, PythonSingle
Maintenance costMediumHigh (custom)
Onboarding speedFast — Gherkin learnable in a daySlow
ReportsHTML, JSON, JUnit, AllureCustom
CommunityLargeNone

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

RoleBrings
Business / PMWhat the feature should do, what value it creates
DeveloperHow to build it, technical constraints, refactoring approach
Tester / QAEdge 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 breakage

For 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

KeywordPurpose
FeatureTop-level description of the feature
RuleGroups scenarios under a business rule (Gherkin 6+)
BackgroundSteps that run before every scenario in the feature
ScenarioA single concrete example of behavior
Scenario OutlineA template scenario with multiple data sets
GivenPreconditions (state before the action)
WhenThe action (the event being tested)
ThenThe expected outcome (the verification)
And / ButContinuation of Given/When/Then
@tagMarker 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/cucumber

Create features/ for .feature files and features/step-definitions/ for JS steps.

Python + pip

pip install behave

Create 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")
  }
}

8. Tags and Hooks

Tags

Tags organize scenarios into logical groups for selective execution:

@smoke @regression
Feature: Login

  @critical
  Scenario: Successful login
    ...

  @edge-case
  Scenario: Login with expired password
    ...

  @wip
  Scenario: Login with SSO
    ...
# Only @smoke
mvn test -Dcucumber.filter.tags="@smoke"

# @regression but not @wip
mvn test -Dcucumber.filter.tags="@regression and not @wip"

# @critical or @smoke
mvn test -Dcucumber.filter.tags="@critical or @smoke"

Hooks

import io.cucumber.java.Before;
import io.cucumber.java.After;

public class Hooks {
  @Before
  public void beforeScenario() { DriverFactory.initDriver(); }

  @Before("@database")
  public void beforeDatabaseScenario() { DatabaseHelper.resetDatabase(); }

  @After
  public void afterScenario() { DriverFactory.quitDriver(); }

  @After("@database")
  public void afterDatabaseScenario() { DatabaseHelper.cleanup(); }
}
HookUse
@BeforeOpen browser, navigate to base URL
@AfterClose browser, take screenshot on failure
@Before("@database")Reset DB before DB-touching scenarios
@Before("@auth")Log in before auth-required scenarios

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/test
const { 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-results

Allure'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

13. Author Bio & Next Steps

About the author: The SoftwareTestPilot Editorial Team is a group of QA practitioners with 40+ years of combined experience. We've shipped BDD suites in banking, retail and SaaS — and we know what works and what wastes time.

Continue Your BDD Journey

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.

Keep going

Practice these questions

Rehearse Selenium and Playwright automation questions covering framework design, waits, locators and CI/CD.

Found this useful?
Share:XLinkedInWhatsApp

Was this article helpful?

Keep building your QA edge

Continue reading

Join the QA Community

Connect with fellow testers, share job leads, and get career advice.

Premium QA Resources

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
4.9/5 rating
Explore All Products

⭐⭐⭐⭐⭐ Trusted by 1,000+ Software Test Pilots • Instant Access