Mert Özen Further With Every Line
Project Management Requirements Analysis — #07

Executable Specifications with Gherkin: Given-When-Then, Rule, Scenario Outline and Writing Declarative Scenarios

Mert Özen Nov 14, 2026 13 min 5 views
Executable Specifications with Gherkin: Given-When-Then, Rule, Scenario Outline and Writing Declarative Scenarios

Gherkin turns the rules and examples found in Example Mapping into a specification that stakeholders can read and a test runner can execute; a good scenario describes the business rule, not the UI. Using Bookstore's institutional order rules, we cover Feature, Rule, Background, Scenario Outline, Examples and data tables, declarative versus imperative scenarios, the Turkish keyword decision and Gherkin anti-patterns.

Version note: The Gherkin files in this post follow Cucumber's Gherkin reference and were run with .NET 10, Reqnroll 3.3.4 and xUnit v3 4.0.3.

Guess First

Emre has written the first scenario for the "Institutional discount" story from post 05 and opened it for review:

Scenario: Discount on a school order
  Given I open "https://localhost:5001/login"
  And I log in as "purchasing@school.example"
  And I click "New institutional order"
  And I paste "9786050000016;40" into the "Lines" text area
  And I click "Check"
  When I click "Calculate total"
  Then I should see "9,000.00" in the "#order-total" element

The scenario runs and it is green. Three questions. What happens to this scenario if Ayşe renames the "Calculate total" button to "Show total"? Can Selin tell which business rule is being tested by reading it? If the institutional discount goes from 10 to 12 percent, how long does it take to find the line that has to change?

BDD: A Conversation, Not a Tool

BDD takes its name from Dan North's "Introducing BDD", published in Better Software magazine in 2006. North's starting point was a problem he kept running into while teaching TDD: the word "test" steered people towards the wrong questions. Talking about behaviour instead of tests, about what the system should do next, made the conversation clearer. The Given-When-Then template appears in the same article as a template North and Chris Matts used to write a story's acceptance criteria as scenarios.

Cucumber's BDD documentation splits the practice into three parts, and that split is also the map of this series' last three posts. Discovery: talking through concrete examples and agreeing on what the system should do; that is exactly what Example Mapping in post 05 was. Formulation: turning those examples into structured documentation that both people and computers can read; the subject of this post. Automation: connecting each example to the system as an automated test; the subject of post 08.

The order matters. Gherkin looks like a test automation language because a test runner executes it in the end. But Gherkin's real readers are Selin and Hakan, not the test runner. A feature file written without discovery turns into a test script that the automation team writes for itself and nobody else reads. Emre's scenario is a small example: it runs, but it states no business rule.

From the Example Map to the Feature File

The example map from post 05 moves into Gherkin almost one to one. The yellow card becomes a Feature, each blue card a Rule, each green card a Scenario. Red cards do not go into the file: something whose answer is unknown cannot be a runnable example. The rounding question was not in the file until accounting answered; once the answer came, it was added with its own scenario. This is the file the Bookstore team wrote for the "Institutional discount" story and for the "if stock is short, the order waits as a whole" slice that stayed in Release 1 in post 05:

@release-1
Feature: Institutional order pricing
  Schools and libraries order a whole term's books in one institutional order.
  The minimum quantity and the institutional discount keep these orders
  worthwhile for both the institution and Bookstore.

  Background:
    Given the catalog contains these books:
      | ISBN          | Title                | List price | Stock | Campaign discount |
      | 9786050000016 | Grade 5 Mathematics  | 250.00     | 500   | 0%                |
      | 9786050000023 | Grade 5 Science      | 84.95      | 500   | 0%                |
      | 9786050000030 | Short Stories Reader | 120.00     | 15    | 25%               |

  Rule: An institutional order contains at least 20 copies in total

    Scenario Outline: The minimum counts copies across all titles
      When the institution orders:
        | Title               | Copies    |
        | Grade 5 Mathematics | <maths>   |
        | Grade 5 Science     | <science> |
      Then the order is <outcome>

      Examples:
        | maths | science | outcome  |
        | 19    | 1       | accepted |
        | 10    | 10      | accepted |
        | 12    | 7       | rejected |

    Scenario: The institution is told how far below the minimum it is
      When the institution orders 19 copies of "Grade 5 Mathematics"
      Then the order is rejected because "at least 20 copies; this order has 19"

  @REQ-BULK-05
  Rule: Institutions get 10% off the list price unless a campaign gives more

    Scenario: The institutional discount is applied to every line
      When the institution orders 40 copies of "Grade 5 Mathematics"
      Then "Grade 5 Mathematics" is charged 9000.00 with a 10% discount
      And the order total is 9000.00

    Scenario: A campaign discount is not added on top of the institutional discount
      When the institution orders 20 copies of "Short Stories Reader"
      Then "Short Stories Reader" is charged 1800.00 with a 25% discount

    Scenario: Each line total is rounded to the cent, half away from zero
      When the institution orders:
        | Title               | Copies |
        | Grade 5 Mathematics | 20     |
        | Grade 5 Science     | 3      |
      Then "Grade 5 Science" is charged 229.37 with a 10% discount
      And the order total is 4729.37

  Rule: Every line must match a book in the catalog

    Scenario: An unknown ISBN is reported with its line number
      When the institution submits this ISBN list:
        | ISBN          | Copies |
        | 9786050000016 | 20     |
        | 9786059999991 | 5      |
      Then the order is rejected because "Line 2: ISBN 9786059999991 was not found in the catalog."

    Scenario: A hyphenated ISBN matches the same book
      When the institution submits this ISBN list:
        | ISBN              | Copies |
        | 978-605-00-0001-6 | 20     |
      Then the order is accepted

  Rule: If any title is short of stock, the whole order waits and ships once

    Scenario: A short title holds the whole order
      When the institution orders:
        | Title                | Copies |
        | Grade 5 Mathematics  | 20     |
        | Short Stories Reader | 30     |
      Then the order is accepted
      And the whole order waits until every title is in stock

Book titles and prices are in English in the code, but the numbers are the same as the green cards in post 05: 12 + 7 is rejected, 40 mathematics books give 9,000, the campaign book gets 25 percent, three science books cost 229.37. None of the scenarios mentions a button, a page or an HTTP status code; all of them say what the institution orders and what the outcome is.

The file as it stands is a test. With the step definitions we will write in post 08, Reqnroll runs this file as ten tests; the three rows of the Scenario Outline count as separate tests:

Test run summary: Passed!
  total: 10
  failed: 0
  succeeded: 10
  skipped: 0
  duration: 852ms

Feature, Rule, Background and Steps

Feature is the title of the file; the free text under it is only a description for Gherkin and is not executed. Use that text to write the story's "why": why institutional orders are valuable to both sides. Someone reading the code sees this first when they open the file.

Rule is the keyword added to Gherkin in version 6 that groups a business rule with the scenarios illustrating it. It gives the most natural match with Example Mapping: every blue card is a Rule. Without Rule the same file turns into a flat list of scenarios, and which scenario illustrates which rule can only be guessed from scenario names.

Background holds shared steps that run before every scenario in the file. Here it defines the three books in the catalog, so each scenario states only what is different about it. The Gherkin reference recommends keeping Background short and, if it grows beyond four lines, moving details into higher-level steps. Ours is a single step with a table; anyone reading the table sees where the prices in the scenarios come from.

Steps start with Given, When and Then; And and But continue the previous keyword. Given sets up a context, When describes a single event, Then states the expected outcome. The reference recommends three to five steps per scenario; ours have two or three, because Background carries most of the context. Some of our scenarios have no Given at all: the context is set up in Background and the scenario has no separate context to state.

The @release-1 at the top of the file and the @REQ-BULK-05 above a rule are tags. Tags group scenarios and let you run them selectively. In the first post we suggested giving requirements short IDs like REQ-BULK-03; putting such an ID on a Rule as a tag reduces the trail from requirement to running test to a single search. In post 08 we will use this tag to run only that rule's scenarios.

Scenario Outline, Examples and Data Tables

The three examples of the minimum quantity rule describe the same behaviour with different numbers. Instead of writing them as three separate scenarios, a Scenario Outline and an Examples table are used: each row of the table produces a scenario by filling in placeholders in angle brackets such as <maths>. Note that placeholders can live in a step's data table, not only in the step text; that way the step is written once and the quantities of the two books come from the row.

The right use of an outline is to show the boundary of one rule at a few points. The table has three rows: 20 copies split unevenly across two books right at the limit, 20 copies split evenly, and 19 copies, one short. Both sides of the boundary and one interesting case; the rule we stated for Example Mapping in post 05 holds here too. What the institution is told when the order is rejected is a separate scenario, because it is a separate behaviour: the content of the message.

A data table is the way to give a step data in list or table form. The catalog, the order lines and the ISBN list are tables in this file; Gherkin does not force you to read the first row as a header, that decision belongs to the step definition. Keep tables narrow enough to understand at a glance. Each of the five columns in the catalog table is used by at least one scenario; an unused column misleads the reader about what matters.

Declarative and Imperative Scenarios

Emre's scenario is imperative: it describes step by step what the user does on the screen. The declarative form of the same behaviour is the "The institutional discount is applied to every line" scenario in the file: the institution orders 40 copies, the line comes to 9,000 with a 10 percent discount. The first says how it is done, the second what happens.

The difference has three concrete consequences. When the UI changes, the declarative scenario does not, because it says nothing about the UI; when the "Calculate total" button is renamed, Emre's scenario turns red although the rule has not changed. When the rule changes, the line to change in the declarative scenario is obvious: the one that says 10 percent. Third, a declarative scenario can be automated at different levels. In post 08 our scenarios will connect directly to the API without a browser; if needed, the same text could one day run as an end-to-end test as well.

A practical test: read the scenario to Hakan, who has never seen the UI. If Hakan can say "yes, that's the rule", the scenario is declarative. If he asks "which button?", the scenario speaks the language of the UI instead of the business.

Turkish Keywords or English?

Gherkin's keywords have been translated into many languages, Turkish among them. Putting # language: tr on the first line of the file makes the keywords read as Turkish. The same minimum rule can be written with Turkish keywords and Turkish step text like this; we parsed and verified this file with Reqnroll too:

# language: tr
Özellik: Kurumsal sipariş fiyatlandırması

  Kural: Kurumsal sipariş toplamda en az 20 adet içerir

    Senaryo: 20 adedin altındaki sipariş reddedilir
      Diyelim ki katalogda "Grade 5 Mathematics" kitabı var
      Eğer ki kurum bu kitaptan 19 adet sipariş ederse
      O zaman sipariş "en az 20 adet" gerekçesiyle reddedilir
      Ve kuruma 1 adet eksik olduğu söylenir

There are two options. Turkish keywords and Turkish step text make the scenario directly readable for Hakan and Gül; the cost is that the matching text in step definitions is Turkish and, in a project where the rest of the code is English, two languages mix in the same files. English keywords and English step text are consistent with the code, and search and tooling work without friction; the cost is that a stakeholder who does not read English has to read the scenario through someone else.

The Bookstore team chooses English, because the project has an English-only code rule and Selin and Hakan, who read the scenarios regularly, can read English. The Turkish explanation keeps living in the story itself, with the rules and examples from post 05. Your team may decide differently; what matters is deciding once and using the same language in every feature file. In Reqnroll the default feature language can also be set with the language.feature setting in reqnroll.json.

Gherkin Anti-Patterns

UI-bound steps: "I click", "I type into", "I should see in the element". This is the problem in Emre's scenario; it makes the scenario brittle and unreadable. UI testing has its place, but that test lives better outside Gherkin, in the Playwright tests we will see in the Integration and E2E Testing series.

A long Background: a ten-line Background hides each scenario's real context. A reader has to scroll up and down to find which Background line an outcome depends on. Keep only the context every scenario really shares in Background; a detail that matters to one scenario should be visible in that scenario's own Given.

Putting everything into a Scenario Outline: a single outline called "Institutional orders" with a twelve-column Examples table tests the minimum quantity, the discount, the campaign and stock on the same rows. Which row illustrates which rule gets lost, and when a row turns red you have to decode the table to understand what broke. An outline exists to show the boundary of one rule; different rules live in different scenarios.

A scenario full of technical terms: "Then the response status is 422 and the JSON body contains errors.lines". This line is meaningless to Selin and depends on an implementation detail of the API. Its business-language equivalent is "the order is rejected because ...", and the HTTP detail stays inside the step definition.

Several When and Then pairs: a scenario shaped like "When ... Then ... When ... Then ..." is testing two behaviours. When the first Then fails, the second behaviour is never tried. Split it into two scenarios.

In Production

Feature files live in the same repository as the code, inside the test project, and are reviewed together with the code. On the Bookstore team, Selin is added as a reviewer to every pull request that changes a feature file; she reads the scenarios, not the code. When a rule changes, the scenario changes first, then the code; if the code changes without the scenario, a red test in CI catches it.

Scenarios run on every CI build. A red scenario tells you one of two things: either the code drifted from the rule, or the rule changed and the file was not updated. In the second case the right response is not to silence the test but to ask the rule's owner.

Organise feature files by feature, not by screen. "Institutional order pricing" is a feature file; "Order screen tests" is not. File names and Feature titles are a table of contents that will later be read as living documentation.

Common Pitfalls

The first pitfall is reducing Gherkin to a file format written only by the test automation team. If scenarios are written without Example Mapping, at the end of the sprint, after the code is finished, BDD's discovery and formulation steps have been skipped; what remains is a more laborious way of writing tests than ordinary tests. In that case xUnit tests are a cheaper and more honest choice.

The second is turning every test case into a scenario. The fifteen edge cases of the ISBN normalisation function are not scenarios Selin needs to read; they live in unit tests. The distribution idea from the Test Strategy series post "Test Pyramid, Testing Trophy and Honeycomb" applies here too: scenarios for a few examples that show a business rule, detailed edge cases for unit tests.

The third is verifying a scenario against database state: "Then the Orders table has a row with Status = 2". That is an implementation detail, and the scenario breaks when the table structure changes. State the outcome in a way the user or the business can observe: the order was accepted, the total is this, the order is waiting for stock.

The fourth is writing scenarios once and freezing them. If an unexpected order from a school in production reveals a new example, that example goes onto the example map first, then into the feature file. The file lives on as the record of the system's current behaviour.

A Small Experiment

★☆☆ Take an imperative scenario or a list of manual test steps from your project and turn it into a declarative scenario. None of the UI words (click, type, page, button) should remain.

★★☆ Turn the example map you drew in the Small Experiment of post 05 into a feature file: the yellow card becomes the Feature, blue cards become Rules, green cards become Scenarios. Gather the examples that show the boundary of the same rule into a Scenario Outline.

★★★ Read a feature file to a stakeholder who has never seen the code. Note the lines where they ask "what does this mean?" and rewrite those lines in business language. Then give the same file to a developer and ask whether the scenarios tell them what code to write.

Our feature file is ready and written in business language. But behind the "ten tests passed" output in this post there is a layer we have not seen yet: the code that binds these sentences to the Bookstore API. In the last post of the series we will set up Reqnroll 3, write step definitions and bindings, connect the scenarios to the API through WebApplicationFactory and generate the living documentation report.