skip to content

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

level: juniorimportance: must knowfreq 82%

answer

  1. a boolean expression, not a list
  2. three lowercase operators plus parentheses
  3. tags travel downward from the line above
  4. not binds tighter than and
  5. a misspelt tag selects nothing

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.

solid answer

~50 s

A Cucumber tag expression is an infix boolean expression over tag names. `@smoke and not @wip` matches scenarios carrying `@smoke` but not `@wip`; `not` binds tighter than `and`, and `and` tighter than `or`, so mix them with parentheses: `(@smoke or @sanity) and not @flaky`. The operators are lowercase words — `&&` and a comma-separated list are not this grammar. You pass the expression to the run: `--tags` on the CLI in Cucumber-JVM and cucumber-js, or the `cucumber.filter.tags` property on the JVM side. Placement matters as much as grammar. Tags go above `Feature`, `Rule`, `Scenario`, `Scenario Outline` and `Examples`, and they are inherited downward, so a tag on the `Feature` line applies to every scenario in the file. You cannot tag a `Background` or an individual step. A misspelt tag matches nothing and Cucumber does not warn you.

code

gherkin · 13 lines
gherkin
@depot @smoke
Feature: Scaffold hire returns

  @wip
  Scenario: Overdue tube bundle is flagged at check-in
    Given a hire of 240 scaffold tubes is 6 days overdue
    When the depot clerk checks the bundle in
    Then the return is flagged for a late-hire charge

  Scenario: On-time return closes the hire
    Given a hire of 96 scaffold tubes is returned on day 13
    When the depot clerk checks the bundle in
    Then the hire is closed with no charge

go deeper

for a junior

Be able to read @smoke and not @wip aloud correctly and to say where a tag is written — on its own line above Feature, Scenario or Examples, never on a step.

for a middle

Explain precedence and inheritance: not before and before or, parentheses when mixing, and a Feature-level tag reaching every scenario in the file.

for a senior

Talk about the silent failure — a typo selects nothing and the build goes green — and how you defend against it with a minimum scenario count and a small agreed tag vocabulary.

for a principal

Own the tag vocabulary itself: who may add one, what each guarantees, and how expressions map to CI jobs so that adding a scenario does not require editing pipeline configuration.

## The grammar A tag expression is a boolean expression whose atoms are tag names, written with the leading `@` exactly as they appear in the feature file: * `@smoke` — carries the tag. * `not @wip` — does not carry the tag. * `@smoke and not @wip` — both conditions. * `@smoke or @sanity` — either. * `(@smoke or @sanity) and not @flaky` — parentheses group, because `not` binds tightest, then `and`, then `or`. The operators are the lowercase words `and`, `or` and `not`. Programming-language operators are not accepted, and neither is a bare comma-separated list of tags. The whole expression is one argument, so on a shell it must be quoted — an unquoted expression is split into words and the run fails or, worse, filters on only the first word. ## Where the expression goes | implementation | how the expression reaches the run | |---|---| | Cucumber-JVM | the `--tags` option on the CLI, or the `cucumber.filter.tags` property | | cucumber-js | the `--tags` option, or the equivalent key in the configuration file | | Behave, SpecFlow/Reqnroll | each has its own tag-filtering option with its own accepted syntax | The practical consequence is that one codebase supports many suites without duplicating anything: the smoke job and the nightly job run the same feature files and differ only in the expression they pass. ## Where the tags themselves go Tags are written on their own line above the keyword they decorate, and several may share a line separated by spaces. They are inherited downward: | tag written above | applies to | |---|---| | `Feature` | every scenario in that file | | `Rule` | every scenario under that rule | | `Scenario` | that scenario | | `Scenario Outline` | every scenario generated from every one of its `Examples` rows | | `Examples` | only the scenarios generated from that block's rows | Two placements are **not** legal: you cannot tag a `Background`, and you cannot tag an individual step. Both attempts are common in review, and both mean the setup or the assertion in question needs a different mechanism rather than a tag. Inheritance is what makes a Feature-level tag so useful and so dangerous. In a scaffolding-hire depot suite, `@depot` on the `Feature` line of the returns file gives all of its scenarios that tag for free; adding `@wip` to one scenario inside it does not remove the inherited `@smoke`, it merely adds a second tag, which is precisely why `and not` is the workhorse operator. A scenario matches `@smoke and not @wip` only if the union of its own tags and its ancestors' tags contains the first and lacks the second. ## The failure nobody notices Cucumber does not validate tag names. There is no registry of legal tags, so a run filtered on `@smok` — one keystroke short of `@smoke` — simply selects nothing. The run finishes in under a second with no failures, and a CI job configured that way can stay green for weeks while testing nothing. Defences: 1. **Assert a floor.** Fail the job when the selected scenario count is below an expected minimum. 2. **Keep the vocabulary tiny and written down.** Four or five tags with agreed meanings beat forty invented ad hoc. 3. **Read the summary line.** "0 scenarios" is a red flag, not a fast build. 4. **Prefer `not` over ever-growing inclusion lists.** `not @wip` keeps working as scenarios are added; `@a or @b or @c` needs editing every time. ## A worked read Given a returns feature whose `Feature` line carries `@depot @smoke`, and whose first scenario also carries `@wip`: under `@smoke and not @wip` the second scenario runs and the first does not, because both inherit `@smoke` from the feature but only the first carries the exclusion. Under `@depot` both run. Under `@smoke and @wip` only the first runs. Working an example like this by hand, out loud, is how the question is usually answered well in an interview. ## Slicing one codebase into several runs Because the expression is an argument rather than a property of the code, the same feature files serve several executions that differ only in what they select. A scaffolding-hire depot suite of 1,246 scenarios might be sliced as: | run | expression | selects | |---|---|---| | pull request | `@smoke and not @wip` | the fast subset, minus anything half-written | | nightly | `not @wip` | everything that is finished | | depot-database job | `@depot-db` | only the scenarios that need seeded stock | Nothing in the feature files changes between those three. That is the mechanical property worth naming in an answer: tags are data on the scenario, expressions are the query, and the query lives with the run configuration rather than with the specification.

  • How do you express "smoke or sanity, but never flaky" as one Cucumber tag expression?
    `(@smoke or @sanity) and not @flaky`. The parentheses are required: without them `not` binds tightest and `and` binds tighter than `or`, so the expression would read as "@smoke, or else @sanity-that-is-not-flaky" and would let flaky smoke scenarios through.
  • A CI job filtered by a tag expression reports zero scenarios and passes. What happened?
    Almost always a tag that matches nothing — a typo, a renamed tag, or an expression whose two halves can never both be true. Cucumber has no registry of valid tags, so it cannot warn you. Fail the job below an expected scenario count, and read the run summary rather than trusting a green tick.
  • Can you put a tag on a Background so its steps only run for some scenarios?
    No. Tags are legal above `Feature`, `Rule`, `Scenario`, `Scenario Outline` and `Examples`, and a `Background` is none of those. Setup that needs to be conditional belongs in a tagged hook instead, where a tag expression decides whether it runs.

saying these in an interview costs you the question

  • Writes && or a comma-separated list instead of and
  • Thinks a Feature-level tag is overridden by a scenario tag
  • Expects Cucumber to reject an unknown or misspelt tag
  • Believes an individual step can carry a tag
  • Mixes and with or and omits the parentheses