Gherkin Keywords ⭐

Keyword Purpose
Feature The feature being described (one per file)
Scenario A single test case
Scenario Outline A data-driven test (runs per Examples row)
Examples The data table for a Scenario Outline
Given Precondition / context
When The action
Then The expected outcome
And / But Continue the previous step type
Background Steps run before every scenario in the file
Rule Groups related scenarios (Gherkin 6+)
# Comment

Feature File Structure

 
gherkin
@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 ⭐ — a Scenario runs once; a Scenario Outline runs once per Examples row.


Data Tables

 
gherkin
Scenario: Create multiple users
  Given the following users exist:
    | name  | email          | role  |
    | Nav   | nav@test.com   | admin |
    | Alex  | alex@test.com  | user  |
 
java
@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"));
    }
}

Doc strings for multi-line text:

 
gherkin
  And the request body is:
    """
    { "id": 1, "name": "Nav" }
    """

Step Definitions ⭐

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

// Cucumber Expressions — {string} {int} {float} {word}
@When("the user enters username {string} and password {string}")
public void enterCredentials(String user, String pass) {
    loginPage.login(user, pass);     // ⭐ call the Page Object
}

@Then("the dashboard should be displayed")
public void dashboardDisplayed() {
    Assert.assertTrue(dashboardPage.isDisplayed());
}

// Regex alternative
@When("^the user clicks the \"([^\"]*)\" button$")
public void clickButton(String button) { }
Expression Matches
{string} A quoted string
{int} An integer
{float} A decimal
{word} A single word
{} Anonymous — any type

⚠️ The rule: step definitions call Page Object methods — no Selenium code in step defs.

Advertisement

Hooks

 
java
@Before                       // before every scenario
public void setUp() { driver = new ChromeDriver(); }

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

@Before("@smoke")             // tagged hook — only for @smoke
public void smokeSetup() { }

@BeforeStep / @AfterStep      // around each step

Order: @BeforeBackgroundGiven/When/Then@After


Tags ⭐

 
gherkin
@smoke @regression
Scenario: ...
 
bash
# Run by tag
--tags "@smoke"
--tags "@smoke and @regression"     # both
--tags "@smoke or @regression"      # either
--tags "not @wip"                   # exclude
--tags "@regression and not @slow"  # combined

Tags can sit on a Feature (applies to all its scenarios) or a single Scenario/Scenario Outline.


The Runner

 
java
@RunWith(Cucumber.class)          // JUnit
// or: extends AbstractTestNGCucumberTests  ← for TestNG + parallel

@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        // true = check for missing step defs only
)
public class TestRunner { }
Option Purpose
features Path to .feature files
glue Package holding step definitions
tags Which scenarios to run
plugin Reporters
monochrome Readable console output
dryRun Validate step defs without executing

Parallel: extend AbstractTestNGCucumberTests and override scenarios() with @DataProvider(parallel = true).


Project Structure

 
src/test/java
├── runners/          TestRunner.java
├── stepdefinitions/  LoginSteps.java
├── pages/            LoginPage.java      ← Selenium lives here
└── utils/            Hooks.java · DriverFactory
src/test/resources
└── features/         login.feature

Common Errors

Error Cause
Undefined step No matching step def — check glue path
Ambiguous step Two step defs match the same text
Duplicate step definition Same annotation text used twice
NullPointerException on driver Missing @Before hook / wrong sharing

Go Deeper