PracticeQAAutomation logoPracticeQAAutomation

🥒 BDD & Cucumber

Tests the whole team can read

Write behaviour in plain English with Gherkin, then automate it. The same two feature files run in three working projects — Java, TypeScript and Python — against this site. Download one, run it, and change it.

BDD in plain words

  • Behaviour-Driven Development is a way of agreeing what software should do before building it, using concrete examples.
  • Business, development and testing (the “three amigos”) write the examples together in Gherkin: Given / When / Then.
  • Cucumber (Java, JavaScript) and Behave (Python) read those files and run matching code — the step definitions.
  • The feature files become living documentation: always up to date, because they are the tests.
  • Honest caveat: BDD pays off when business people read the scenarios. If only testers do, it's an extra layer — plain tests may be simpler.

Gherkin cheat sheet

Feature
— The capability being described, with a short “As a … I want … So that …” story.
Background
— Steps that run before every scenario in the file — shared setup.
Scenario
— One concrete example of the behaviour.
Given / When / Then
— Given the starting state, When the user acts, Then the outcome. And / But continue the previous kind.
Scenario Outline + Examples
— One scenario run once per row of a table — data-driven tests without copy-paste.
Data table
— A table passed into one step, e.g. a list of products to add.
@tags
— Labels for choosing what to run: @smoke, @regression, @wip.

Write behaviour, not clicks

Scenarios should say what the user does and expects. Selectors and clicks belong in the step definitions, so a UI change touches one step — not every feature file.

✗ Imperative — breaks when the UI changes

Scenario: Login
  Given I open "https://…/login"
  When I type "student" into the "#username" field
  And I type "Password123" into the "#password" field
  And I click the "Log in" button
  Then the element ".success" is visible

✓ Declarative — describes behaviour

Scenario: Successful login
  Given I am on the login page
  When I log in as "student" with password "Password123"
  Then I am welcomed as "student"

The practice project

Two features against this site: the login form and Nova Shop's cart and coupons — 10 scenarios, including a Scenario Outline and a data table. Every project below runs all 10 green; set HEADLESS=true to hide the browser.

1 · The feature files (shared by all three stacks)

login.feature
Feature: Log in to the practice form
  As a student
  I want to log in with my username and password
  So that I can reach my account

  Background:
    Given I am on the login page

  @smoke
  Scenario: Successful login
    When I log in as "student" with password "Password123"
    Then I am welcomed as "student"

  Scenario Outline: Login is rejected
    When I log in as "<username>" with password "<password>"
    Then I see the error "<error>"

    Examples:
      | username | password    | error                                    |
      | student  | wrong       | Invalid username or password.            |
      |          | Password123 | Both username and password are required. |
      | student  |             | Both username and password are required. |

  Scenario: The account locks after three wrong passwords
    When I enter a wrong password 3 times
    Then I see the error "Too many failed attempts. Account locked — refresh to try again."
    And the login button is disabled

2 · The step definitions — pick your stack

⬇ Download the Java projectNeeds Java 17+, Maven, Chrome · run mvn test
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- PracticeQAAutomation — BDD starter: Cucumber + Selenium + TestNG (Java 17+). MIT License. -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.practiceqaautomation</groupId>
  <artifactId>bdd-cucumber-selenium</artifactId>
  <version>1.0.0</version>

  <properties>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <cucumber.version>8.0.3</cucumber.version>
  </properties>

  <dependencies>
    <dependency><groupId>io.cucumber</groupId><artifactId>cucumber-java</artifactId><version>${cucumber.version}</version><scope>test</scope></dependency>
    <dependency><groupId>io.cucumber</groupId><artifactId>cucumber-testng</artifactId><version>${cucumber.version}</version><scope>test</scope></dependency>
    <dependency><groupId>io.cucumber</groupId><artifactId>cucumber-picocontainer</artifactId><version>${cucumber.version}</version><scope>test</scope></dependency>
    <!-- Cucumber 8 needs a JSON library to write its reports -->
    <dependency><groupId>tools.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>3.2.3</version><scope>test</scope></dependency>
    <dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.49.0</version><scope>test</scope></dependency>
    <dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>7.12.0</version><scope>test</scope></dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.6.0</version>
      </plugin>
    </plugins>
  </build>
</project>

Real problems we hit building these — and the fixes

Chrome's password pop-ups (Selenium)

After a login with a simple password, Chrome may show “Save password?” or “password found in a data breach”. That pop-up takes the keyboard and mouse, so every later step silently fails. Turn it off with Chrome prefs — see Hooks.java / environment.py. Playwright does this for you.

Typing before the page is ready

React pages can discard what you type before they finish loading. Wait for a ready signal — this site sets <html data-app-ready="true"> — instead of sleeping.

Cucumber-JVM 8 needs a JSON library

Without one it stops with “Cucumber needs a JSON library to write reports”. The pom.xml adds Jackson for this reason.

Empty values in Behave examples

Behave's default matcher won't match "" — the login step uses a regular expression so an empty username still matches.

Sharing state between step classes

Don't use static fields. Cucumber-JVM injects one TestContext per scenario (PicoContainer); Cucumber.js uses a World; Behave uses context.

Preparing for interviews? See the BDD & Cucumber interview questions. The starter projects are free under the MIT licence.