When does a broad Cucumber step expression stop being reuse and become accidental capture?
answer
- One match is never reported
- Ask what the parameter carries
- Data varies, intent does not
- A conditional in the body is the confession
- Close the vocabulary with a parameter type
basics
~20 sReuse is two steps meaning the same action with a different value. Capture is one loose expression being the only match for a step it was never written for - Cucumber says nothing and the step passes wrongly.
solid answer
~40 sThe parameter is the test. A shared step definition is healthy when the captured text is genuinely **data** the same action varies over - a quantity, a variety name, a date. It is accidental capture when the captured text carries **intent**: an expression such as `the {string} order is {string}` swallows *placed*, *cancelled* and *dispatched* into one method, and whichever branch that method takes is the one every scenario silently gets. Nothing warns you, because Cucumber only complains when *two* definitions match; one over-broad definition matching alone is a clean, green, wrong step. Narrow it by closing the vocabulary - a registered parameter type over an enumerated set - or by splitting the definition per intent. Free-text captures such as an unbounded regex group are the usual culprit.
code
java · 17 lines// over-broad: one definition swallows placed, cancelled and anything else
@Given("the {string} order is {string}")
public void orderIs(String reference, String state) {
if (state.equals("cancelled")) { catalogue.cancel(reference); }
else { catalogue.place(reference); }
}
// narrowed: an enumerated parameter type, so an unknown word no longer matches
@ParameterType("placed|cancelled|dispatched")
public OrderState orderState(String word) {
return OrderState.valueOf(word.toUpperCase());
}
@Given("the {string} order is {orderState}")
public void orderIs(String reference, OrderState state) {
catalogue.apply(reference, state);
}go deeper
Know that a Cucumber step definition can match more sentences than its author intended, and that a parameter should hold a value the rule varies over rather than the action itself.
Explain the three outcomes - no match, two matches, one over-broad match - and why only the third is silent. Show how a registered parameter type over an enumerated set turns a silent wrong run into a reported undefined step.
Bring a case where a green suite was wrong for days and describe the audit you ran afterwards: grouping matched step texts per definition, hunting conditionals in bodies, and narrowing without collapsing back into one definition per sentence.
Own the standard: what the team is allowed to parameterise, how new step expressions are reviewed, and how you trade the maintenance cost of more definitions against the risk of a definition nobody can prove is matching only what it should.
Every Cucumber suite reaches a moment where one definition serves several step lines. Sometimes that is the parameterisation that keeps the glue layer small; sometimes it is one loose expression quietly eating steps it was never written for. From the feature file the two look identical, which is what makes this worth interviewing on. ## Why capture is silent Cucumber matches the text after the keyword against every registered expression. Two matches is a reported problem. **One** match is success - and Cucumber has no way to know that the sentence it matched means something the method was not written to do. The step goes green having run the wrong branch, and it stays green, because the assertion in the following `Then` was written by the same person who wrote the loose expression. | Situation | What Cucumber does | |---|---| | No definition matches | Reports the step as undefined and offers a generated snippet | | Two definitions match | Reports the clash and fails the run | | One over-broad definition matches | Runs it, reports success - no signal at all | That third row is the whole problem. There is no diagnostic; the only detection is a human reading the definition. ## The test that separates them > Do the step lines sharing this definition describe the **same action with a different value**, or **different actions that happen to share a sentence shape**? Healthy reuse: `the buyer orders 3 packets` and `the buyer orders 17 packets`. Same action, the number is data. Accidental capture: `the "spring-bulbs" order is placed` and `the "spring-bulbs" order is cancelled` under `the {string} order is {string}`. The second capture is not data - it is the verb. One method now switches on a word from the feature file, and every future sentence of that shape ("...is refunded", "...is disputed") lands in it too and does whatever the fallback branch happens to do. ## What makes an expression too broad - **A free-text capture where an enumeration belongs.** `{string}` or a `.*` group holding a state, an action or a screen name rather than a value. - **Capturing the verb.** If the parameter changes what the method *does* rather than what it does it *to*, the expression is too broad by construction. - **Alternation used to bridge meanings.** Alternation and optional text exist for grammar - singular versus plural, a dropped article. Using them to make one expression cover two different behaviours is capture wearing a costume. - **A regex whose groups absorb whatever is left.** Cucumber matches the whole step text, so a pattern with `.*` at the end will happily consume a trailing clause that changes the meaning of the sentence. - **A definition with a boolean or string switch in its body.** The body is the confession: a conditional on a captured word means two definitions were merged. ## A worked case On a seed-catalogue ordering service the definition `the {string} order is {string}` was written for *placed* and grew a branch for *cancelled*. A refactor renamed the cancellation path and the branch silently fell through to placement. The suite ran green on a 3-way parallel fork, and because a merge queue serialised everything, 19 merges landed on top of the false green before a support ticket surfaced the bug. Total elapsed time from the rename to detection: 6 days. The step that should have failed was one of the most reused in the suite - which is exactly why nobody suspected it. ## Narrowing without losing reuse 1. **Close the vocabulary.** Register a parameter type over an enumerated set so an unexpected word fails to match and the step is reported undefined rather than silently run. In Cucumber-JVM that is a `@ParameterType` method; cucumber-js has an equivalent registration call. 2. **Split by verb, keep the noun parameterised.** `the {string} order is placed` and `the {string} order is cancelled` are two definitions sharing one order-reference parameter. Reuse is preserved exactly where it was real. 3. **Prefer typed captures.** `{int}` over a free-text group for a quantity, a registered type over `{string}` for a domain concept. 4. **Audit by grouping.** Cucumber-JVM ships a formatter that lists each step definition together with the step texts that matched it. Read that grouping on an inherited suite: any definition with a long, semantically varied list under it is the first thing to inspect. ## The opposite failure Over-narrowing is real too, and it is what pushes teams to loose expressions in the first place: one definition per sentence, four near-identical methods differing by a number. The answer to that is a typed parameter over the value, not a wildcard over the verb. Reuse the shape when the meaning is the same; duplicate the shape when the meaning is not.
- You inherit a suite and suspect accidental capture. How do you find it without reading every definition?Group step texts by the definition that matched them - Cucumber-JVM can report that grouping directly. Definitions whose matched lines vary in meaning rather than in a value are the candidates, and any body containing a conditional on a captured word confirms it. Sort by the number of distinct matched sentences and start at the top.
- Two teams want the same step sentence to mean different things. What do you do?Do not let one definition arbitrate. Either the sentence is genuinely one action and the difference is data - then parameterise it and share - or the meanings differ, in which case the sentences must differ too. Rewording one team's step so it states its own intent is cheaper than a definition that branches on which team wrote the scenario.
- Does adding a stricter regex instead of a Cucumber Expression solve accidental capture?Only if the pattern actually enumerates the accepted words. A regex is not inherently narrower - most captures written as regular expressions use wide groups. What removes the silence is a closed set of accepted values, so an unexpected word fails to match and surfaces as an undefined step rather than running the wrong branch.
saying these in an interview costs you the question
- Assumes Cucumber warns when one loose expression matches
- Counts matched step texts as evidence of good reuse
- Captures the verb of the sentence as a parameter
- Branches on a captured word inside the definition body
- Uses alternation to make one expression cover two behaviours
- Believes a regular expression is automatically narrower