Gherkin & Cucumber — Complete Quick Reference

Gherkin Keywords

Keyword Purpose
Feature Describes the functionality being tested; normally one Feature per .feature file
Scenario Represents a single test case
Scenario Outline Represents a data-driven test that runs once for each row in Examples
Examples Provides test data for a Scenario Outline
Given Defines the precondition or initial context
When Defines the action being performed
Then Defines the expected result
And Continues the previous step type
But Continues the previous step type with contrast/exception
Background Contains common steps executed before every scenario in the Feature
Rule Groups related scenarios around a specific business rule; available in Gherkin 6+
# Used to add comments

Feature File Structure

A .feature file describes the behavior of an application using business-readable language.

@login @smoke
Feature: User Login
  As a registered user
  I want to log in
  So that I can access my dashboard

  Background:
    Given the user is on the login page

  @positive
  Scenario: Login with valid credentials
    When the user enters username "admin" and password "admin123"
    And clicks the login button
    Then the dashboard should be displayed

  @negative
  Scenario Outline: Login with invalid credentials
    When the user enters username "<username>" and password "<password>"
    And clicks the login button
    Then the error message "<message>" should be displayed

    Examples:
      | username | password | message              |
      | admin    | wrong    | Invalid password     |
      | unknown  | admin123 | User not found       |
      |          | admin123 | Username is required |

Scenario vs Scenario Outline ⭐

Type Execution
Scenario Executes once
Scenario Outline Executes once for every row in the Examples table

For example, the above Scenario Outline executes 3 times because there are 3 rows of test data.


Data Tables

Data Tables are useful when you need to pass multiple pieces of structured data to a step.

Advertisement

Feature File

Scenario: Create multiple users
  Given the following users exist:
    | name | email        | role  |
    | Nav  | nav@test.com | admin |
    | Alex | alex@test.com | user  |

Step Definition

@Given("the following users exist:")
public void createUsers(DataTable table) {

    List<Map<String, String>> rows =
            table.asMaps(String.class, String.class);

    for (Map<String, String> row : rows) {
        System.out.println(
            row.get("name") + " - " + row.get("role")
        );
    }
}

Common DataTable Conversion

List<Map<String, String>> rows =
        table.asMaps(String.class, String.class);

This converts the table into a list of maps.

For example:

row.get("name")
row.get("email")
row.get("role")
 

Doc Strings

Doc Strings are used when you need to pass multi-line text to a step.

A common use case is passing a JSON request body.

Feature File

Scenario: Create user through API
  When the user sends the following request body:
    """
    {
      "id": 1,
      "name": "Nav"
    }
    """

Step Definition

@When("the user sends the following request body:")
public void sendRequestBody(String requestBody) {

    System.out.println(requestBody);
}

Step Definitions ⭐

Step Definitions connect Gherkin steps with Java automation code.

The important rule is:

Step Definitions should call Page Object methods. Selenium code should remain inside the Page Objects.

Example

@Given("the user is on the login page")
public void userOnLoginPage() {
    driver.get("https://example.com/login");
}

Cucumber Expressions

Cucumber Expressions allow dynamic values to be passed from feature files.

@When("the user enters username {string} and password {string}")
public void enterCredentials(String user, String pass) {

    loginPage.login(user, pass);
}

The Feature File:

When the user enters username "admin" and password "admin123"

passes:

user = admin
pass = admin123

Expected Result

@Then("the dashboard should be displayed")
public void dashboardDisplayed() {

    Assert.assertTrue(
        dashboardPage.isDisplayed()
    );
}

Cucumber Expression Parameters

Expression Matches
{string} Quoted string
{int} Integer
{float} Decimal number
{word} Single word
{} Anonymous parameter; accepts any type

Example

@When("the user enters {string}")
public void enterText(String text) {
    System.out.println(text);
}
 
@When("the user enters OTP {int}")
public void enterOtp(int otp) {
    System.out.println(otp);
}
 
@When("the user enters amount {float}")
public void enterAmount(float amount) {
    System.out.println(amount);
}

Regular Expression Alternative

Instead of Cucumber Expressions, you can use regular expressions.

@When("^the user clicks the \"([^\"]*)\" button$")
public void clickButton(String button) {

    System.out.println("Clicked: " + button);
}

For new projects, Cucumber Expressions are generally easier to read and maintain.


Step Definition Best Practice ⭐

❌ Avoid this

@When("the user enters username {string}")
public void enterUsername(String username) {

    driver.findElement(
        By.id("username")
    ).sendKeys(username);
}

Here, Selenium implementation is directly inside the step definition.

✅ Prefer this

@When("the user enters username {string}")
public void enterUsername(String username) {

    loginPage.enterUsername(username);
}

Page Object:

public void enterUsername(String username) {

    driver.findElement(
        By.id("username")
    ).sendKeys(username);
}
Feature File
     ↓
Step Definition
     ↓
Page Object
     ↓
Selenium WebDriver
     ↓
Application

This keeps the framework clean and maintainable.


Hooks

Hooks are used to execute setup and cleanup code before or after scenarios.

@Before

Runs before every scenario.

@Before
public void setUp() {
    driver = new ChromeDriver();
}

@After

Runs after every scenario.

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

Tagged Hooks

You can execute a hook only for scenarios having a particular tag.

@Before("@smoke")
public void smokeSetup() {

    System.out.println("Smoke test setup");
}

This hook runs only for:

@smoke
Scenario: Login test

@BeforeStep and @AfterStep

These hooks execute around every individual step.

@BeforeStep
public void beforeStep() {
    System.out.println("Before step");
}
 
@AfterStep
public void afterStep() {
    System.out.println("After step");
}

Hook Execution Order

@Before
     ↓
Background
     ↓
Given
     ↓
When
     ↓
Then
     ↓
@After

If @BeforeStep / @AfterStep are used:

@Before
     ↓
Background
     ↓
@BeforeStep
     ↓
Step
     ↓
@AfterStep
     ↓
@BeforeStep
     ↓
Step
     ↓
@AfterStep
     ↓
@After

 Tags 

Tags allow you to categorize and selectively execute scenarios.

@smoke @regression
Scenario: User login

You can also tag an entire Feature:

@smoke
Feature: User Login

The tag will apply to the scenarios inside that Feature.

Common Tag Expressions

Run smoke tests:

--tags "@smoke"

Run scenarios having both tags:

--tags "@smoke and @regression"

Run scenarios having either tag:

--tags "@smoke or @regression"

Exclude WIP scenarios:

--tags "not @wip"

Combined expression:

--tags "@regression and not @slow"

Cucumber Runner ⭐

Cucumber can be integrated with JUnit or TestNG.

JUnit Runner

@RunWith(Cucumber.class)
@CucumberOptions(
    features = "src/test/resources/features",
    glue = "stepdefinitions",
    tags = "@smoke",
    plugin = {
        "pretty",
        "html:target/cucumber-report.html",
        "json:target/cucumber.json"
    },
    monochrome = true,
    dryRun = false
)
public class TestRunner {
}

TestNG

For TestNG-based frameworks:

public class TestRunner
        extends AbstractTestNGCucumberTests {
}

You can also configure Cucumber options using the appropriate Cucumber/TestNG setup.


@CucumberOptions

Option Purpose
features Specifies the location of .feature files
glue Specifies the package containing Step Definitions and Hooks
tags Selects scenarios based on tags
plugin Configures reporting/output plugins
monochrome Makes console output easier to read
dryRun Checks whether all steps have matching step definitions

Example

@CucumberOptions(
    features = "src/test/resources/features",
    glue = "stepdefinitions",
    tags = "@smoke",
    plugin = {
        "pretty",
        "html:target/cucumber-report.html",
        "json:target/cucumber.json"
    },
    monochrome = true,
    dryRun = false
)

dryRun

dryRun is useful for checking missing step definitions.

dryRun = true

dryRun = true

Cucumber checks whether every Gherkin step has a matching Step Definition without actually executing the test.

dryRun = false

dryRun = false

The test executes normally.

Easy Interview Answer

dryRun = true is used to validate whether all feature-file steps have corresponding step definitions without executing the actual test steps.


Cucumber Reports

Common plugins include:

plugin = {
    "pretty",
    "html:target/cucumber-report.html",
    "json:target/cucumber.json"
}

pretty

Provides readable console output.

html

Generates an HTML report.

json

Generates JSON output that can be consumed by reporting tools or CI pipelines.


Parallel Execution ⭐

With TestNG, Cucumber scenarios can be executed in parallel using a parallel Data Provider.

Example:

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

Runner:

public class TestRunner
        extends AbstractTestNGCucumberTests {

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

Important

For parallel execution, your WebDriver must be thread-safe.

A common approach is:

private static ThreadLocal<WebDriver> driver =
        new ThreadLocal<>();

This prevents different parallel tests from sharing the same WebDriver instance.


A clean Cucumber + Selenium framework can look like this:

src
├── test
│   ├── java
│   │   ├── runners
│   │   │   └── TestRunner.java
│   │   │
│   │   ├── stepdefinitions
│   │   │   └── LoginSteps.java
│   │   │
│   │   ├── pages
│   │   │   └── LoginPage.java
│   │   │
│   │   └── utils
│   │       ├── Hooks.java
│   │       └── DriverFactory.java
│   │
│   └── resources
│       └── features
│           └── login.feature

Responsibility of Each Layer

Layer Responsibility
features Business-readable test scenarios
stepdefinitions Connect Gherkin to Java code
pages Selenium locators and UI actions
utils Driver, configuration, reusable utilities
runners Cucumber execution/configuration
Hooks Setup, teardown, screenshots, etc.

Common Cucumber Errors

Error Cause Solution
Undefined step No matching Step Definition exists Check the step text and glue path
Ambiguous step More than one Step Definition matches the same step Remove or modify the duplicate matching definitions
Duplicate step definition Same step definition exists multiple times Keep only one matching definition
NullPointerException on driver Driver was not initialized or is not shared correctly Check @Before, DriverFactory, and driver lifecycle
Steps not detected Incorrect glue package Verify the package path
Feature not found Incorrect Feature path Verify the features location
Scenario not running Incorrect tag expression Check the tags configuration
Browser closes unexpectedly Incorrect driver lifecycle Check @After and DriverFactory

Complete Execution Flow ⭐

The overall Cucumber + Selenium execution flow is:

.feature file
      ↓
Feature / Scenario
      ↓
Cucumber Runner
      ↓
Tag Filtering
      ↓
@Before Hook
      ↓
Background
      ↓
Step Definition
      ↓
Page Object
      ↓
Selenium WebDriver
      ↓
Web Application
      ↓
Expected Result
      ↓
@After Hook
      ↓
Cucumber Report
 

21. Most Important Cucumber Interview Points ⭐

Scenario vs Scenario Outline

A Scenario executes once, while a Scenario Outline executes once for every row in the Examples table.

Background

Background contains common preconditions that execute before every scenario in the Feature.

DataTable

DataTable is used to pass structured tabular data from a Feature File to a Step Definition.

Doc String

A Doc String is used to pass multi-line text, such as JSON, XML, or request bodies, from a Feature File to a Step Definition.

Hooks

Hooks execute setup and teardown logic before or after scenarios or individual steps.

Tags

Tags are used to categorize scenarios and selectively execute tests, such as smoke, regression, or sanity tests.

Glue

glue tells Cucumber where to find Step Definitions and Hooks.

Dry Run

dryRun = true validates whether Feature File steps have corresponding Step Definitions without executing the tests.

Page Object Model

Step Definitions should contain business-level actions and delegate UI interactions to Page Objects rather than directly using Selenium.

Parallel Execution

Cucumber scenarios can be executed in parallel with TestNG using a parallel Data Provider, provided the WebDriver and test state are thread-safe.


Go Deeper