skip to content

Scenario Outlines & Data Tables

Running one scenario over many rows with Examples, and passing structured data into a step as a data table or a doc string. Interviewers test whether you parameterise instead of duplicating.

on this pageshow

explore

questions

5

In a Cucumber Scenario Outline, where does an <placeholder> from an Examples row get substituted?

level: juniorimportance: must knowfreq 76%

answer

  1. one scenario per Examples row
  2. the outline name is templated too
  3. inner tables and doc strings substitute
  4. tags and Background are not templated
  5. substitution happens before step matching

basics

~20 s

Cucumber substitutes an Examples value into the Scenario Outline's name, into step text, into the cells of a data table attached to a step, and into a doc-string body. Tags, comments and Background are never templated.

solid answer

~40 s

Gherkin expands a `Scenario Outline` into one runnable scenario per `Examples` row before anything executes. For each row, `<placeholder>` is replaced by the cell under the matching header column in four places: the outline's **name**, the **step text**, the cells of a **data table** attached to a step, and the body of a **doc string** attached to a step. Nothing else is templated. Tags, comment lines, the `Feature:` line and the `Background:` block see no substitution, which is why you cannot write `@<segment>` to tag rows selectively. Substitution is purely textual and happens before step matching: Cucumber inserts the raw cell content and only then matches the resulting step text against your step definitions, so an empty cell yields an empty string and can stop a step matching at all.

code

gherkin · 11 lines
gherkin
Scenario Outline: Bill a <tariff> account for <kwh> kWh
  Given the account is on the "<tariff>" tariff
  When the meter reports:
    | period  | kwh   |
    | 2026-01 | <kwh> |
  Then the invoice total is <total>

  Examples:
    | tariff   | kwh  | total  |
    | Standard | 1843 | 214.37 |
    | Assisted | 1843 | 168.92 |

go deeper

for a junior

Be ready to name the four places a value lands: the outline's name, the step text, an attached data table's cells and an attached doc string. Knowing that the name can be templated already puts you ahead of most screening answers.

for a middle

Explain the mechanics: expansion happens at parse time, one scenario per row, matched by column header name, and the substituted text is what the step definition matches against. Say plainly what is not templated and why.

for a senior

Show the diagnostic habit. Read the generated step text rather than the template when an outline misbehaves, recognise an empty cell producing an undefined step, and treat untemplated outline names as a reporting defect on a large nightly pack.

for a principal

Own the convention. Decide whether outline names must carry a discriminating placeholder so reports stay readable, and where the line sits between an Examples table and a fixture the suite loads, before dozens of feature files harden around whichever habit forms first.

## What a Scenario Outline actually is A `Scenario Outline` is not a loop and it is not a scenario. It is a **template** paired with one or more `Examples` tables, and the Gherkin parser expands it *before* anything executes: one runnable scenario per data row, with every `<placeholder>` replaced by that row's cell. Everything downstream — the runner, the tag filter, the hooks, the formatters — sees ordinary scenarios and has no idea an outline produced them. That single fact explains almost every behaviour people find surprising here. Substitution is matched by **column name**, not by position. `<tariff>` takes the value of the column whose header cell reads `tariff`. A placeholder with no matching column is simply left in place as literal text, which is the usual cause of a step that fails with an angle bracket still visible in the report. ## The four places a value lands | Location | Substituted? | Why it matters | |---|---|---| | The `Scenario Outline:` name | Yes | Each generated scenario gets a distinct name in the report | | The text of a step inside the outline | Yes | The commonest use, and the only one many candidates know | | The cells of a data table attached to a step | Yes | One Examples row can drive a whole inner table | | The body of a doc string attached to a step | Yes | Lets one payload template vary per row | | Tags on the outline or on an `Examples` block | No | Tag names are literal; `@<segment>` is not a template | | Comment lines | No | Comments never reach the pickle | | The `Feature:` line and the `Background:` block | No | Both sit outside the template | The two rows people miss are the data table and the doc string. A table written *underneath a step inside the outline* is templated cell by cell, so a single Examples row can populate a multi-row table argument. The same is true of a doc-string body. ## What is never templated - **Tags.** You cannot write `@<segment>` above the outline and expect per-row tagging. Gherkin treats it as a literal tag name attached to every generated scenario. Row-group selection is done by splitting the rows into a second `Examples` block and tagging that block. - **The `Background:` block.** Background belongs to the feature, not to the outline, so no placeholder is substituted into it. It still runs before every generated scenario — with identical text every time. - **Comments** and the `Feature:` line. - **Your step-definition code.** The glue never sees a placeholder; by the time matching happens the text is already final. ## Substitution is textual, and it happens first The order is: parse the feature file, expand each outline into scenarios, filter by tag expression, then match each step's final text against the step definitions. Three consequences follow. 1. **Values are inserted verbatim, with no typing.** If a step definition expects a quoted value, the quotes must be written around the placeholder in the step text — `"<tariff>"` — because the cell contributes only its characters. 2. **An empty cell yields an empty string.** Inside quotes that is harmless and the step still matches with an empty value. Standing bare where a number was expected, the step text no longer matches any definition at all, and the failure is reported as an undefined step rather than as a data problem — a confusing signature until you have seen it once. 3. **Cell content follows Gherkin's escaping rules.** Padding whitespace added to align the table is trimmed, a literal pipe inside a cell is written `\|`, a newline is written `\n`, and a literal backslash is `\\`. ## Across the implementation family Cucumber-JVM and cucumber-js share the same Gherkin parser, so expansion is identical in both. Behave and SpecFlow/Reqnroll parse Gherkin with their own front ends; step text, inner tables and doc strings behave the same in practice, but treat placeholder support in unusual positions as implementation-specific and check it rather than assuming parity. ## Why this comes up in interviews Because the name is templated, and most teams do not use that. A district-heating billing team with a 148-scenario nightly pack had a tariff outline of 23 Examples rows whose name was the bare text `Bill an account`; the report showed 23 identically-named entries and, three weeks before a contractor handover, nobody could tell which tariff had regressed. Putting `<tariff>` and `<kwh>` in the outline's name cost one line and made the report readable again. The second reason is diagnostic. When a candidate knows that expansion happens before matching, they debug a broken outline by reading the *generated* step text rather than staring at the template — and they know instantly that a `Background` step cannot be the place to consume a row value.

  • A teammate writes @<segment> above a Scenario Outline hoping to tag each Examples row differently. What actually happens?
    Nothing useful. Tags are never templated, so Gherkin treats `@<segment>` as a literal tag name that lands on every scenario the outline generates, and no row is selected separately. The way to select a group of rows is to move them into a second `Examples` block and tag that block instead.
  • One cell of an Examples table is left empty. How does the generated scenario behave?
    The placeholder becomes an empty string. If it sat inside quotes in the step text, the step still matches and your glue receives an empty value. If it stood bare where a number was expected, the resulting step text matches no definition and Cucumber reports the step as undefined, which reads as a glue problem when it is really a data problem.
  • Can a Background step use a value from the Examples row?
    No. `Background` sits outside the `Scenario Outline`, so it is not part of the template and no placeholder is substituted into it. It runs before every generated scenario with identical text. If a setup step genuinely needs the row value, move it into the outline body where substitution applies.

The Examples table is a mail-merge source: Cucumber fills the blanks in the body of the letter, never the routing labels on the envelope.

saying these in an interview costs you the question

  • Thinks placeholders are substituted into tags
  • Says a Scenario Outline loops internally over its rows
  • Believes Background steps see the Examples row values
  • Thinks the Examples table is passed to the step as an argument
  • Assumes substitution is type-aware rather than plain text
open as a page

In Cucumber-JVM, how does a data-table step argument become the parameter type your step method declares?

level: middleimportance: must knowfreq 64%

basics

~20 s

The parameter type your step method declares drives the conversion: a list of lists gives raw rows and no header, a list of maps keys each row by the first row, and a domain-type list needs a registered transformer.

open as a page

In Cucumber, what does a tag on a single Examples block select that a tag on the Scenario Outline cannot?

level: middleimportance: should knowfreq 44%

basics

~20 s

A tag above one Examples block applies only to the scenarios generated from that block's rows, so it can select a group of rows. A tag on the Scenario Outline applies to every row of every Examples block below it.

open as a page

In Cucumber-JVM, how do you choose between a @DataTableType per domain type and a default entry transformer?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Register a @DataTableType per type when the table's columns are not the object's fields or you want unknown columns to fail loudly. Register one default entry transformer when many types bind field-for-field. With neither, conversion fails naming the type.

open as a page

In Cucumber-JVM, how does a doc string's content type reach a @DocStringType transformer?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

Write the content type immediately after the doc string's opening delimiter in the feature file. Cucumber-JVM pairs it with the step method's declared parameter type to select a @DocStringType transformer. With none registered, the body arrives as a plain String.

open as a page