TL;DR

  • Cucumber enables BDD with tests written in plain English (Gherkin syntax)
  • Feature files describe behavior: Given (preconditions), When (actions), Then (outcomes)
  • Step definitions link Gherkin steps to actual test code
  • Scenario Outlines enable data-driven testing with Examples tables
  • Integrates with Selenium, TestNG, JUnit for complete test automation

Best for: Teams wanting business-readable tests, collaboration between QA and stakeholders Skip if: Small team where developers write all tests (traditional testing faster) Reading time: 15 minutes

Your test cases are in Jira. Test code is in the repository. Business requirements are in Confluence. Nobody knows if tests actually cover requirements. Every sprint, QA explains the same scenarios to stakeholders.

Cucumber bridges this gap. Tests are written in plain English. Business stakeholders can read them. Developers implement them. Everyone speaks the same language.

This tutorial covers Cucumber from Gherkin basics to CI/CD integration — everything for implementing BDD in your team.

What is BDD?

Behavior Driven Development (BDD) is a development approach where tests are written in natural language that describes business behavior. Tests become living documentation that everyone understands.

BDD benefits:

  • Shared understanding — business, QA, and dev speak same language
  • Living documentation — tests describe current system behavior
  • Early defect detection — requirements are validated before coding
  • Reduced rework — clear specifications prevent misunderstandings

What is Cucumber?

Cucumber is a tool that supports BDD by executing plain-text test specifications written in Gherkin. It connects human-readable scenarios to automated test code.

How Cucumber works:

  1. Write feature files in Gherkin (plain English)
  2. Create step definitions (code that implements each step)
  3. Run Cucumber — it matches steps to definitions and executes tests
  4. Get reports showing which scenarios passed/failed

Setup

Java (Maven)

<dependencies>
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-java</artifactId>
        <version>7.15.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-testng</artifactId>
        <version>7.15.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.17.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

JavaScript (npm)

npm install @cucumber/cucumber --save-dev

Project Structure

src/test/
├── java/
│   └── com/example/
│       ├── steps/
│       │   └── LoginSteps.java
│       ├── pages/
│       │   └── LoginPage.java
│       └── runners/
│           └── TestRunner.java
└── resources/
    └── features/
        └── login.feature

Gherkin Syntax

Feature File

# login.feature
Feature: User Login
  As a registered user
  I want to log into my account
  So that I can access my dashboard

  Background:
    Given the user is on the login page

  Scenario: Successful login with valid credentials
    When the user enters email "user@example.com"
    And the user enters password "password123"
    And the user clicks the login button
    Then the user should see the dashboard
    And the welcome message should contain "Welcome"

  Scenario: Failed login with invalid password
    When the user enters email "user@example.com"
    And the user enters password "wrongpassword"
    And the user clicks the login button
    Then the user should see an error message "Invalid credentials"

Keywords

KeywordPurposeExample
FeatureDescribes the feature being testedFeature: User Login
ScenarioSingle test caseScenario: Successful login
GivenPreconditions (setup)Given the user is on the login page
WhenActions performedWhen the user clicks login
ThenExpected outcomesThen the dashboard is displayed
And/ButAdditional stepsAnd the user is logged in
BackgroundCommon steps for all scenariosRuns before each scenario

Step Definitions

Basic Steps

// LoginSteps.java
package com.example.steps;

import io.cucumber.java.en.*;
import static org.testng.Assert.*;

public class LoginSteps {

    private LoginPage loginPage;
    private DashboardPage dashboardPage;

    @Given("the user is on the login page")
    public void userIsOnLoginPage() {
        loginPage = new LoginPage(driver);
        loginPage.navigateTo();
    }

    @When("the user enters email {string}")
    public void userEntersEmail(String email) {
        loginPage.enterEmail(email);
    }

    @When("the user enters password {string}")
    public void userEntersPassword(String password) {
        loginPage.enterPassword(password);
    }

    @When("the user clicks the login button")
    public void userClicksLoginButton() {
        dashboardPage = loginPage.clickLogin();
    }

    @Then("the user should see the dashboard")
    public void userShouldSeeDashboard() {
        assertTrue(dashboardPage.isDisplayed());
    }

    @Then("the welcome message should contain {string}")
    public void welcomeMessageShouldContain(String expectedText) {
        String actualMessage = dashboardPage.getWelcomeMessage();
        assertTrue(actualMessage.contains(expectedText));
    }

    @Then("the user should see an error message {string}")
    public void userShouldSeeErrorMessage(String expectedError) {
        String actualError = loginPage.getErrorMessage();
        assertEquals(actualError, expectedError);
    }
}

Parameter Types

// Custom parameter types
@ParameterType("enabled|disabled")
public String status(String status) {
    return status;
}

@Given("the feature is {status}")
public void featureIsStatus(String status) {
    if (status.equals("enabled")) {
        // enable feature
    }
}

// Using regex
@When("the user waits for {int} seconds")
public void userWaitsForSeconds(int seconds) {
    Thread.sleep(seconds * 1000);
}

// Doc strings for multi-line input
@When("the user enters the following address:")
public void userEntersAddress(String address) {
    addressPage.enterAddress(address);
}

Scenario Outline (Data-Driven Testing)

Feature: Login Validation

  Scenario Outline: Login with various credentials
    Given the user is on the login page
    When the user enters email "<email>"
    And the user enters password "<password>"
    And the user clicks the login button
    Then the login result should be "<result>"
    And the message should be "<message>"

    Examples:
      | email              | password    | result  | message              |
      | user@example.com   | password123 | success | Welcome back         |
      | admin@example.com  | admin123    | success | Welcome admin        |
      | user@example.com   | wrongpass   | failure | Invalid credentials  |
      |                    | password123 | failure | Email is required    |
      | user@example.com   |             | failure | Password is required |
      | invalid-email      | password123 | failure | Invalid email format |
@Then("the login result should be {string}")
public void loginResultShouldBe(String expectedResult) {
    if (expectedResult.equals("success")) {
        assertTrue(dashboardPage.isDisplayed());
    } else {
        assertTrue(loginPage.hasError());
    }
}

@Then("the message should be {string}")
public void messageShouldBe(String expectedMessage) {
    String actual = getCurrentMessage();
    assertEquals(actual, expectedMessage);
}

Hooks

// Hooks.java
package com.example.steps;

import io.cucumber.java.*;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class Hooks {

    private static WebDriver driver;

    @BeforeAll
    public static void beforeAll() {
        // Run once before all scenarios
        System.out.println("Starting test suite");
    }

    @Before
    public void before(Scenario scenario) {
        // Run before each scenario
        driver = new ChromeDriver();
        driver.manage().window().maximize();
        System.out.println("Starting: " + scenario.getName());
    }

    @Before("@smoke")
    public void beforeSmoke() {
        // Run only for scenarios tagged @smoke
        System.out.println("Running smoke test");
    }

    @After
    public void after(Scenario scenario) {
        // Run after each scenario
        if (scenario.isFailed()) {
            // Take screenshot on failure
            byte[] screenshot = ((TakesScreenshot) driver)
                .getScreenshotAs(OutputType.BYTES);
            scenario.attach(screenshot, "image/png", "failure-screenshot");
        }
        driver.quit();
    }

    @AfterAll
    public static void afterAll() {
        // Run once after all scenarios
        System.out.println("Test suite completed");
    }

    public static WebDriver getDriver() {
        return driver;
    }
}

Tags

@regression
Feature: User Management

  @smoke @critical
  Scenario: Create new user
    Given I am logged in as admin
    When I create a new user
    Then the user should be created

  @slow
  Scenario: Generate user report
    Given I am on the reports page
    When I generate a full user report
    Then the report should be downloaded

Running Tagged Scenarios

# Run only smoke tests
mvn test -Dcucumber.filter.tags="@smoke"

# Run regression but not slow tests
mvn test -Dcucumber.filter.tags="@regression and not @slow"

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

Page Object Integration

// LoginPage.java
public class LoginPage {
    private WebDriver driver;
    private WebDriverWait wait;

    private By emailField = By.id("email");
    private By passwordField = By.id("password");
    private By loginButton = By.id("login-btn");
    private By errorMessage = By.className("error");

    public LoginPage(WebDriver driver) {
        this.driver = driver;
        this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    }

    public void navigateTo() {
        driver.get("https://example.com/login");
    }

    public void enterEmail(String email) {
        wait.until(ExpectedConditions.visibilityOfElementLocated(emailField))
            .sendKeys(email);
    }

    public void enterPassword(String password) {
        driver.findElement(passwordField).sendKeys(password);
    }

    public DashboardPage clickLogin() {
        driver.findElement(loginButton).click();
        return new DashboardPage(driver);
    }

    public String getErrorMessage() {
        return wait.until(ExpectedConditions.visibilityOfElementLocated(errorMessage))
            .getText();
    }

    public boolean hasError() {
        return driver.findElements(errorMessage).size() > 0;
    }
}

Test Runner

TestNG Runner

// TestRunner.java
package com.example.runners;

import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
import org.testng.annotations.DataProvider;

@CucumberOptions(
    features = "src/test/resources/features",
    glue = "com.example.steps",
    plugin = {
        "pretty",
        "html:target/cucumber-reports/report.html",
        "json:target/cucumber-reports/report.json"
    },
    tags = "@smoke or @regression"
)
public class TestRunner extends AbstractTestNGCucumberTests {

    @Override
    @DataProvider(parallel = true)
    public Object[][] scenarios() {
        return super.scenarios();
    }
}

JUnit 5 Runner

import org.junit.platform.suite.api.*;
import static io.cucumber.junit.platform.engine.Constants.*;

@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("features")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "com.example.steps")
@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty, html:target/report.html")
public class RunCucumberTest {
}

CI/CD Integration

GitHub Actions

name: Cucumber Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Run Cucumber tests
        run: mvn test -Dcucumber.filter.tags="@smoke"

      - name: Publish report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: cucumber-report
          path: target/cucumber-reports/

Cucumber with AI Assistance

AI tools can help write and maintain Cucumber tests.

What AI does well:

  • Generate Gherkin scenarios from user stories
  • Create step definitions from feature files
  • Suggest test data for scenario outlines
  • Identify missing edge cases

What still needs humans:

  • Defining business-relevant scenarios
  • Balancing coverage vs maintainability
  • Validating scenarios with stakeholders
  • Refactoring duplicate steps

FAQ

What is Cucumber?

Cucumber is a BDD (Behavior Driven Development) testing tool that allows you to write tests in plain English using Gherkin syntax. Feature files describe application behavior that both technical and non-technical team members can understand. Step definitions connect these human-readable specifications to actual test automation code.

What is Gherkin?

Gherkin is Cucumber’s domain-specific language for writing test scenarios in plain text. It uses keywords like Feature, Scenario, Given, When, Then to structure tests in a readable format. The syntax is designed to be understood by everyone on the team — developers, QA engineers, product managers, and business stakeholders.

Is Cucumber free?

Yes, Cucumber is completely free and open-source. It’s available for multiple programming languages including Java, JavaScript, Ruby, Python, and Kotlin. There are no paid versions or enterprise features — all functionality is available at no cost.

Cucumber vs Selenium — what’s the difference?

Cucumber and Selenium serve different purposes and work together. Cucumber defines what to test using Gherkin scenarios (the specification). Selenium performs browser automation (the execution). In a typical setup, Cucumber scenarios describe the expected behavior, and step definitions use Selenium to interact with the browser and verify outcomes.

Official Resources

See Also