skip to content

Cucumber & BDD Automation

The tool family that turns Gherkin scenarios into executable tests: feature files, step definitions and living docs across the JVM, JS, Python and .NET. SDET specs name it, so interviewers probe it.

on this pageshow

explore

questions

page 1 of 2

In a Cucumber .feature file, which lines does the Gherkin parser treat as keywords, and what happens to a line that matches none?

level: juniorimportance: must knowfreq 80%

answer

  1. A closed vocabulary, not free prose
  2. Free text is legal in exactly one place
  3. Description sits under a keyword line
  4. One Feature keyword per .feature file
  5. Anything else stops the file parsing

basics

~20 s

Gherkin recognises a fixed keyword set - Feature, Rule, Background, Scenario, Scenario Outline, Examples and the Given/When/Then/And/But step keywords. Other text is free-form description where the grammar allows it, and a parse error that fails the whole file anywhere else.

solid answer

~40 s

The Gherkin parser accepts a closed vocabulary: `Feature`, `Rule`, `Background`, `Scenario` (or `Example`), `Scenario Outline`, `Examples`, the step keywords `Given`/`When`/`Then`/`And`/`But`/`*`, plus tag lines starting `@`, whole-line `#` comments, `|` table rows and the `"""` doc-string delimiter. Everything else is free-form **description** text - but only in one position: directly under a `Feature`, `Rule`, `Background`, scenario or `Examples` line, until the next keyword line. Elsewhere it is a syntax error, and Cucumber fails to read the whole file rather than skipping the offending scenario. The structural rules that go with this: one `Feature` per file; at most one `Background` per `Feature` and per `Rule`, positioned before that container's scenarios; and comments must occupy a whole line, since Gherkin has no trailing-comment syntax.

code

gherkin · 12 lines
gherkin
# Menus freeze at the 09:47 cut-off
Feature: School meal ordering
  Parents order meals until the daily cut-off.
  The kitchen headcount is frozen after it.

  Background:
    Given the kitchen can prepare 318 meals

  Scenario: Order lands before the cut-off
    Given the clock reads 09:41
    When a parent orders 2 vegetarian meals
    Then the order is accepted

go deeper

for a junior

Be ready to list the keywords from memory and to say where each may appear. Interviewers open with this because everything else in Cucumber rests on it, and hesitating here reads as never having written a feature file.

for a middle

Explain the mechanics: description text is legal only under a keyword line, one Feature per file, Background before the scenarios of its container, comments whole-line only. Say clearly that an unrecognised line fails the file at parse time rather than skipping a scenario.

for a senior

Show you have debugged this. A parse failure surfaces as a run with no scenario results, which reads like an infrastructure fault; be ready to describe how you spot it fast and how you keep non-engineers editing feature files without breaking them.

for a principal

Own the policy: who may edit feature files, whether the parse runs in CI on every change to the feature directory, and how narrative is kept in description blocks so a shared suite stays readable to the domain experts it is written for.

## The lines Gherkin treats as keywords A `.feature` file is not free prose with some keywords sprinkled in. Cucumber parses it with the Gherkin parser, which recognises a **closed vocabulary** and gives every other line one of exactly two fates. In the English dialect the recognised line starts are: | Line begins with | Gherkin calls it | Where it is legal | |---|---|---| | `Feature:` | the feature header | once per file, at the top | | `Rule:` | a business-rule grouping | between `Feature` and its scenarios | | `Background:` | shared setup steps | once per `Feature`, once per `Rule`, before that container's scenarios | | `Scenario:` / `Example:` | one concrete example | under `Feature` or under `Rule` | | `Scenario Outline:` / `Scenario Template:` | a parameterised scenario | same places as `Scenario` | | `Examples:` / `Scenarios:` | the rows of an outline | under the outline it belongs to | | `Given` `When` `Then` `And` `But` `*` | step keywords | inside `Background`, `Scenario` or an outline | | `@` | a tag line | on its own line above `Feature`, `Rule`, a scenario or `Examples` | | `#` | a whole-line comment | anywhere | | `\|` | a table row | as a step argument or inside `Examples` | | `"""` or triple backticks | a doc-string delimiter | directly under a step | `Scenario` and `Example` are synonyms, as are `Examples` and `Scenarios`, and `Scenario Outline` and `Scenario Template`. The step keyword is **not** part of what a step definition matches — it is stripped before matching — which is why `Given`, `And` and `*` are interchangeable to the glue layer and matter only to the human reader. ## The structural rules the parser enforces 1. **One `Feature` per file.** A second `Feature:` line is a parse error, not a second feature. If a merge leaves two of them in one file, split the file. 2. **`Background` is positional.** At most one per container, and it must come before the first scenario of that container. A `Background` written after a scenario fails to parse. 3. **Steps need a home.** A `Given`/`When`/`Then` line before any `Background` or scenario has nothing to attach to and fails. 4. **Comments are whole-line only.** The `#` must be the first non-whitespace character on the line. Gherkin has no trailing-comment syntax, so `When the cut-off passes # 09:47` is a step whose text ends in `# 09:47`, and your step definition now has to match that. 5. **Tags live on their own line** above the element they decorate; a tag written after text on a keyword line is not a tag. ## What happens to a line that matches nothing Gherkin allows **free-form description text** in one position: immediately after a `Feature:`, `Rule:`, `Background:`, scenario or `Examples:` line, running until the next keyword line. Those lines are kept as that element's description and are never executed. This is where the narrative belongs — the paragraph explaining who the feature is for, or a link to the rule it implements. Anywhere else, an unrecognised line is a **parse error**. The parser reports the file, the line and the kinds of token it expected there, and that file does not run. The failure is a *reading* failure, so it happens before any scenario starts: you do not get "one bad scenario, the rest pass". That is the single most useful thing to know about this rule, because the instinct — carried over from languages where an unknown line is a comment or a no-op — is that Cucumber will simply skip it. ## Why it bites on a shared suite Consider a school-meal ordering service whose feature files are edited by both the platform team and the kitchen-operations team. Someone pastes a two-line note between the `When` and the `Then` of a scenario to explain a cut-off time. Locally nothing looks wrong; the file is still readable English. On CI the whole file stops parsing, and because the run fails at discovery the report shows no scenario results at all — which reads like an infrastructure problem rather than a syntax one. The fix is a one-character edit: put a `#` in front of the note, or move it into the description block under `Scenario:`. Two habits keep this cheap: - **Parse in CI on every change** to the feature directory, so a malformed file is caught by the build rather than by whoever runs the suite next. - **Teach the description block.** Non-engineers editing feature files want to write prose; give them the one legal place for it instead of relying on them to remember which lines are keywords. ## Checking yourself The compact way to hold this: Gherkin is closer to a **form with named fields** than to prose. Each keyword opens a field, the parser knows which fields may follow which, and the only free text it accepts is the description blank each field leaves for you. Everything else is either a step, a table row, a tag, a comment, a doc-string delimiter — or an error that stops the file.

  • Where may a comment go in a Gherkin feature file, and can you put one at the end of a step line?
    Comments are whole-line only: the `#` must be the first non-whitespace character. Gherkin has no trailing-comment syntax, so `When the cut-off passes # 09:47` is a step whose text ends in `# 09:47`, and the step definition would have to match that text. Put the note on its own `#` line, or in the description block.
  • A bad merge leaves two Feature lines in one file. What does Cucumber do?
    It fails to parse the file. The Gherkin grammar allows at most one `Feature` per file, so the second `Feature:` line is a syntax error rather than a second feature. Nothing in the file runs. The fix is to split the content into two `.feature` files.
  • A product owner wants two paragraphs of narrative in a feature file. Where do they go?
    In the description block: free-form lines directly under the `Feature:`, `Rule:` or scenario line, ending at the next keyword line. They are kept as that element's description and never executed. Whole-line `#` comments are the other option. What breaks is prose placed between steps, which is a parse error.

A feature file is closer to a form with named fields than to an essay: each keyword opens a field, and the only free text the parser accepts is the description blank each field leaves for you.

saying these in an interview costs you the question

  • Thinks Gherkin silently ignores any line it cannot parse
  • Believes several Feature keywords can share one file
  • Puts a trailing # comment on the end of a step line
  • Thinks a syntax error only skips the offending scenario
  • Treats a feature file as prose with keywords sprinkled in
open as a page

What does Cucumber's tag expression "@smoke and not @wip" select, and which lines can carry those tags?

level: juniorimportance: must knowfreq 82%

basics

~20 s

It selects scenarios tagged @smoke that are not also tagged @wip. Cucumber tag expressions are boolean, with lowercase and, or, not and parentheses. Tags written on Feature, Rule, Scenario, Scenario Outline and Examples lines are inherited downward.

open as a page

In Cucumber-JVM, what does the glue option point at, and what happens when it is wrong?

level: juniorimportance: must knowfreq 68%

basics

~20 s

The glue option lists package names that Cucumber scans recursively for step definitions and hooks — packages, never file paths. Point it at the wrong package and every step of every scenario reports undefined, not just one line.

open as a page

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

level: juniorimportance: must knowfreq 76%

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.

open as a page

In Cucumber, how do you select a formatter and point it at an output file, and which built-in formats are machine-facing?

level: juniorimportance: must knowfreq 66%

basics

~20 s

Cucumber-JVM registers a formatter with --plugin name:path or the cucumber.plugin property; cucumber-js uses --format name:path; Behave pairs --format with --outfile. Console and HTML output is written for people, while JSON, JUnit XML and message output is written for tools.

open as a page

In cucumber-js, what is the World object and how long does one World instance live?

level: juniorimportance: must knowfreq 62%

basics

~20 s

The World is cucumber-js's per-scenario context object, reachable as this inside step definition and hook functions. Cucumber builds a fresh World before every scenario and throws it away afterwards, so one scenario's state never reaches the next.

open as a page

In a Cucumber Expression, what do {int}, {string}, {word} and {} each capture?

level: juniorimportance: must knowfreq 72%

basics

~20 s

In a Cucumber Expression, {int} captures a whole number and passes an integer, {string} captures quoted text and strips the quotes, {word} captures one token with no whitespace, and {} captures anything and converts it to the method parameter's type.

open as a page

In cucumber-js, what locates your step definitions, and what does setWorldConstructor change?

level: middleimportance: must knowfreq 58%

basics

~20 s

cucumber-js loads support code from the paths in its import or require options, defaulting to files beside the feature files; loading a module is what registers its steps. setWorldConstructor replaces the per-scenario World, which steps reach through this.

open as a page

In Cucumber-JVM, what does each hook level — @Before, @BeforeStep, @BeforeAll — wrap, and which still run after a step fails?

level: middleimportance: must knowfreq 74%

basics

~20 s

@Before and @After wrap one scenario, @BeforeStep and @AfterStep wrap every individual step, and @BeforeAll and @AfterAll run once per JVM around the whole run. @After still runs when a step or a @Before hook fails.

open as a page

How do you declare a Cucumber-JVM suite on the JUnit Platform, and how does it find the feature files?

level: middleimportance: must knowfreq 76%

basics

~20 s

Annotate a plain class with the JUnit Platform's @Suite and @IncludeEngines("cucumber"), then point it at the feature resources with @SelectClasspathResource. Cucumber's JUnit Platform engine must be on the test classpath; it then creates one test per scenario.

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 should a step definition body call instead of driving widgets directly?

level: middleimportance: must knowfreq 74%

basics

~20 s

A Cucumber step definition should call one intent-shaped method on an automation layer beneath it, such as a service client or a page object. Selectors, waits and driver calls belong in that layer, never in the glue class itself.

open as a page

How does Cucumber-JVM decide whether a step definition string is a Cucumber Expression or a regex?

level: middleimportance: must knowfreq 68%

basics

~20 s

Cucumber-JVM reads the annotation string as a regular expression when it starts with a caret, ends with a dollar sign, or is wrapped in forward slashes; otherwise it parses it as a Cucumber Expression. In cucumber-js the argument type decides.

open as a page

In Behave, what does features/environment.py define, and what is the context object?

level: juniorimportance: should knowfreq 42%

basics

~20 s

Behave loads features/environment.py and calls the hook functions it finds there by name: before_all, before_feature, before_scenario, before_step, before_tag and their after_ counterparts. Each is handed the same Context object, layered so scenario-level attributes vanish when the scenario ends.

open as a page

In Cucumber, what does a conjunction step like "Given I sign in and open the catalogue" cost you?

level: juniorimportance: should knowfreq 63%

basics

~20 s

Cucumber strips the keyword and matches the whole remaining sentence, so a conjunction step becomes one definition nothing else can reuse, and a failure cannot say which half broke. Split it into two steps with two definitions.

open as a page

In SpecFlow/Reqnroll, what does [Binding] mark, and how does context injection supply scenario state?

level: middleimportance: should knowfreq 44%

basics

~20 s

[Binding] marks a class whose step definitions and hooks SpecFlow/Reqnroll discover in the test assembly. Context injection builds those classes once per scenario from a scenario-scoped container, so several binding classes share one instance of a state class.

open as a page

What does Gherkin's Rule keyword group, and how does a Background under a Rule differ from one at Feature level?

level: middleimportance: should knowfreq 42%

basics

~20 s

Rule groups the scenarios that illustrate one business rule, sitting between Feature and its scenarios. A Rule may carry its own Background, which applies only to that Rule's scenarios and runs after the feature-level Background, never instead of it.

open as a page

In Cucumber-JVM, which wins when the same cucumber.* option is set in cucumber.properties and by @ConfigurationParameter?

level: middleimportance: should knowfreq 52%

basics

~20 s

The value closest to the run wins. On a JUnit Platform suite, @ConfigurationParameter is supplied with that suite's own discovery, so it beats a cucumber.properties file on the classpath root; that file is the weakest layer, a project-wide default.

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

What does a single line of Cucumber's Messages NDJSON stream represent, and what consumes that stream?

level: middleimportance: should knowfreq 37%

basics

~20 s

Each line is one JSON envelope holding exactly one message: a parsed feature file, a compiled runnable scenario, a step result, an attachment. Cucumber's built-in HTML, JSON and JUnit formatters all consume that same stream rather than writing independently.

open as a page

In Cucumber-JVM with PicoContainer, how do two step definition classes share one state object?

level: middleimportance: should knowfreq 57%

basics

~20 s

Both step definition classes declare the shared type as a constructor parameter. With cucumber-picocontainer present, PicoContainer builds the glue classes per scenario, creating one instance of that type and injecting the same reference into every class that asks.

open as a page

In Cucumber, how do undefined, ambiguous and pending step results differ, and which one prints a snippet?

level: middleimportance: should knowfreq 58%

basics

~20 s

Undefined means no step definition matched the line, and Cucumber prints a snippet stub for it. Ambiguous means more than one matched, so the runner refuses to choose. Pending means a matched definition signalled unfinished work. All three stop the scenario.

open as a page

Which parts of a Cucumber-family suite actually port between cucumber-js, Behave and Reqnroll?

level: seniorimportance: should knowfreq 36%

basics

~20 s

The .feature file ports, because the Gherkin grammar and its dialect data are shared across the family. Almost nothing below it does: step definitions, hooks, the per-scenario state object, data-table access, tag-filter syntax, configuration files and report formats are all implementation-specific.

open as a page

A Cucumber doc string carrying a YAML payload broke after the feature file was reindented - how does Cucumber decide which leading whitespace it strips?

level: seniorimportance: should knowfreq 33%

basics

~20 s

Cucumber removes from each doc-string line up to as many leading whitespace characters as the opening triple-quote delimiter is indented by. Relative indentation survives, so moving the body relative to the delimiter silently changes a whitespace-sensitive payload.

open as a page

In Cucumber-JVM, what can an @After hook do with the Scenario object it receives?

level: seniorimportance: should knowfreq 48%

basics

~20 s

The Scenario object both reports and writes: getName and getSourceTagNames identify the scenario, getStatus and isFailed give the outcome once the steps have run, attach adds bytes with a media type and a name, log adds text.

open as a page

In Cucumber, how do you make a @Before hook run only for scenarios carrying one tag, and when does that beat a Background?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Give the hook a tag expression: @Before("@depot-db") in Cucumber-JVM, Before({tags: '@depot-db'}, fn) in cucumber-js. Unlike a Background, that setup is conditional, reaches scenarios in every feature file, and stays out of the reader's scenario text.

open as a page

What do Cucumber-JVM's parallel execution settings switch on, and what must the glue satisfy first?

level: seniorimportance: should knowfreq 57%

basics

~20 s

They tell Cucumber's own JUnit Platform engine to run scenarios concurrently inside one JVM, with a strategy that sizes the pool. The unit is the scenario, each Examples row included, so every scenario must own its state.

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

How do you wire Allure or Serenity BDD onto a Cucumber-JVM run, and what must your CI job still do itself?

level: seniorimportance: should knowfreq 51%

basics

~20 s

Allure attaches as an ordinary Cucumber plugin, writing intermediate result files that the Allure command line later renders into HTML. Serenity BDD wraps the run and needs a separate aggregation step. CI must clear stale results and archive the output.

open as a page

Cucumber reruns Background before every Examples row - when does that setup belong in a hook instead?

level: seniorimportance: should knowfreq 49%

basics

~20 s

Background runs before every scenario, and each Examples row is its own scenario - so four Background steps above a 23-row outline run 92 times. Keep Background for context a reader needs; move machinery nobody reads into a tag-filtered hook.

open as a page

showing 1–30 of 43