Testing

Gherkin and Given-When-Then: specification that becomes a test

Gherkin is the language behind BDD: a small set of keywords that makes a test scenario readable by anyone, from the Product Owner to the developer, and at the same time executable by tools like Cucumber, Behave (Python), SpecFlow (.NET), or Behat (PHP). It's the point where the specification and the test become the same document.

The keywords

The whole grammar fits in a few words. The rest is free text that you choose:

  • Feature — the grouping; describes a capability of the system.
  • Scenario — a concrete example of behavior.
  • Given — the context / precondition. The world before the action.
  • When — the action that triggers the behavior. Just one per scenario, ideally.
  • Then — the observable outcome that gets verified.
  • And / But — chain steps of the same type, keeping the reading fluid.

The Given-When-Then structure

The heart of Gherkin is separating context → action → outcome. This triad forces the author to think about behavior, not screens:

# language: en
Feature: User authentication

  Scenario: Login with valid credentials
    Given I am on the login page
    And I have an account with the email "ana@example.com"
    When I enter the correct credentials
    Then I should see the dashboard
    And I should see the greeting "Hello, Ana"
Golden rule: Given has no action, When is a single action, Then has no side effects, it only verifies. If a scenario has three "When"s, it's probably three scenarios in disguise.

Background: the context that repeats

When several scenarios share the same Givens, the Background avoids repetition; it runs before each scenario in the file:

  Background:
    Given I am logged in as an administrator
    And a product "Notebook" exists in the catalog

Scenario Outline: one scenario, lots of data

To test the same rule with several inputs (the classic case of equivalence partitioning and boundary values), use the Scenario Outline with an Examples table:

  Scenario Outline: Discount by order value
    Given a cart with <value> dollars
    When the customer checks out
    Then the total charged should be <total> dollars

    Examples:
      | value | total |
      | 100   | 100   |
      | 101   | 90.9  |
      | 150   | 135   |

Each row of the table becomes an independent scenario. It's the cleanest way to cover boundaries (the 100 that gets no discount and the 101 that does) without copy and paste.

From text to code: step definitions

Gherkin alone tests nothing; it gets "glued" to the system by step definitions: functions that match each step by expression and run the actual automation. In Cucumber with JavaScript:

const { Given, When, Then } = require('@cucumber/cucumber');
const { expect } = require('@playwright/test');

Given('a cart with {int} dollars', async function (value) {
  this.cart = createCart(value);
});

When('the customer checks out', async function () {
  this.total = this.cart.checkout();
});

Then('the total charged should be {float} dollars', async function (expected) {
  expect(this.total).toBeCloseTo(expected, 2);
});

Notice that the same step a cart with {int} dollars serves every row of the Examples. Well-written steps are reusable, and that's BDD's maintenance payoff.

Declarative > imperative

The most common mistake is writing imperative scenarios that describe every click. They break with every screen tweak and nobody on the business side can read them:

Imperative (brittle)Declarative (robust)
When I fill in the "email" field with "ana@x.com"
And I fill in the "password" field with "123"
And I click the "#btn-login" button
When I log in with valid credentials

The "how to fill in the form" belongs to the step definition (and to the Page Object), not to Gherkin. The scenario describes the intent.

Anti-patterns to watch for

  • Soap-opera scenario: ten steps covering an entire flow. One scenario = one behavior.
  • UI detail in the Then: verifying a button's color or CSS. Verify a business outcome.
  • Coupling between scenarios: scenario 2 depending on the state left by scenario 1. Each one must be independent.
  • Gherkin without conversation: writing the .feature files alone, after the implementation. That's just a dressed-up report, and you lose the discovery value of BDD.

Conclusion

Gherkin is simple on purpose: the vocabulary restriction is what keeps the scenario readable for everyone and stable over time. Mastering Given-When-Then, Background, and Scenario Outline, and knowing how to write declaratively, is what turns a BDD suite into living documentation instead of one more set of brittle tests.

Read also

Back to the blog