skip to content

Cucumber-JVM & Runner Integration

How a Cucumber-JVM suite is declared on the JUnit Platform or TestNG, and how cucumber.properties and configuration parameters decide glue, plugins and parallelism. Interviewers ask for the wiring.

on this pageshow

explore

questions

6

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

level: juniorimportance: must knowfreq 68%

answer

  1. Two things share the name glue
  2. It is a package, not a path
  3. The scan reaches subpackages too
  4. Uniform failure means configuration, local means code
  5. A hook outside the scan never fires

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.

solid answer

~50 s

`cucumber.glue` — the `--glue` argument on the command-line runner, the `glue` element of the JUnit 4 options annotation — is a comma-separated list of **package names**. Cucumber loads the classes in those packages and their subpackages and registers the methods carrying step-definition and hook annotations; type registrations are picked up in the same scan. Two failures then look alike and are not. A wrong or missing glue package makes *every* step of *every* scenario undefined and prints a suggested snippet for each distinct step: a uniform wall of undefined steps is a configuration symptom, not a coding gap. A genuinely missing step definition is local — the steps above it pass, that one line is undefined, and the rest of the scenario is skipped. The quiet variant is worse: a hook class outside the glue never runs, so setup silently does not happen.

code

java · 7 lines
java
// wrong: a source folder path is not a package name
@ConfigurationParameter(key = GLUE_PROPERTY_NAME,
                        value = "src/test/java/com/vinylmarket/steps")

// right: packages, comma separated, each scanned recursively
@ConfigurationParameter(key = GLUE_PROPERTY_NAME,
                        value = "com.vinylmarket.steps,com.vinylmarket.hooks")

go deeper

for a junior

Be able to say that the glue option takes package names, that the scan includes subpackages, and that the feature path and the glue package are two different values. That is the everyday knowledge here.

for a middle

Explain the diagnostic difference between a wrong glue path and a genuinely missing step definition, and why an unregistered hook fails silently rather than loudly.

for a senior

Show how you keep an uncheckable string safe in a real repository: a narrow dedicated package, one place that sets the option, and a dry-run stage that turns a misconfiguration into a fast build failure.

for a principal

Own the layout convention across teams so that glue paths stay narrow, and decide whether step definitions may be shared between suites at all — the wide glue path that makes sharing easy is the same one that couples teams together.

## Two meanings of one word "Glue" is overloaded in Cucumber-JVM, and interviews exploit it. It means (a) the step-definition and hook **code** that binds Gherkin lines to Java, and (b) the **option** that tells Cucumber where that code lives. This question is about the option. Its value is a package name, and everything confusing about it follows from the fact that a package name is just a string: the compiler cannot check it, a refactor will not rename it, and a typo produces a run that works perfectly and finds nothing. You set it in whichever place your entry point uses — `@ConfigurationParameter` with the glue key on a JUnit Platform suite, `--glue` on the command-line runner, the `glue` element on the JUnit 4 or TestNG options annotation, or the `cucumber.glue` key in a properties file. Multiple packages are comma-separated. ## What the scan actually collects Cucumber loads every class in each listed package **and all its subpackages**, then registers: - methods annotated with step keywords, such as the `@Given`, `@When` and `@Then` annotations of the English language package; - hook methods — `@Before`, `@After`, `@BeforeStep`, `@AfterStep`, `@BeforeAll`, `@AfterAll`; - parameter-type and data-table type registrations declared in those classes; - the classes themselves as candidates for instantiation, with the dependency-injection container chosen separately. Note what is *not* in that list: feature files. The glue option and the feature selector are two different values pointing at two different things, and mirroring your package layout in `src/test/resources` makes them look deceptively alike. ## Wrong glue versus a genuinely undefined step This is the diagnostic that separates someone who has run Cucumber from someone who has read about it. | Symptom | Wrong glue path | Missing step definition | |---|---|---| | Scope | every step of every scenario | one step line | | Steps before it | also undefined | pass normally | | Steps after it | undefined | skipped | | Suggested snippets | one per distinct step in the whole run | one | | Fix | correct the package or the classpath | write the definition | A run where the first `Given` of a scenario is undefined while nothing else in the run is defined either is a configuration failure. A run where two scenarios pass and a third has one undefined line is a genuine gap. Reading that difference off the summary saves half an hour of poking at expressions that were never the problem. ## The six ways it goes wrong 1. **A source path instead of a package.** `src/test/java/com/vinylmarket/steps` is a directory; `com.vinylmarket.steps` is the package. Only the second is a legal glue value. 2. **Code in another module.** The glue package is spelled correctly but the module holding it is not a test dependency, so nothing is on the classpath to scan. 3. **A rename that missed the string.** Someone moves the step classes; the IDE updates every import and leaves the glue string pointing at a package that no longer exists. 4. **Glue too wide.** Pointing at a parent package pulls in another team's definitions, which is how a suite acquires duplicate matches — the step-definition side of that is its own topic, but the *cause* is often an over-broad glue path. 5. **Hooks outside the glue.** A `@Before` that starts a session and lives one package outside the scan simply never fires. There is no error, only a null reference a few steps later. 6. **Relying on a default.** Whatever a given entry point falls back to when the option is unset, depending on it couples your step layout to where the runner class happens to sit. Set it explicitly. ## Keeping it honest A few conventions remove almost all of this: - keep step definitions and hooks in one dedicated package tree, and point glue at its root; - set the option in exactly one place per entry point, so nobody has to work out which layer won; - keep the glue package narrower than the package holding unrelated test helpers; - run the suite with Cucumber's dry-run option in CI, which walks discovery and matching without executing step bodies, so a broken glue path fails fast and loudly instead of surfacing as a wall of undefined steps in a nightly run. The last one is the habit worth naming in an interview. Dry run turns a configuration mistake into a build failure that takes seconds, which is exactly the treatment a string the compiler cannot check deserves.

  • Your @Before hook never runs, but every step passes. What is happening?
    The hook class sits outside every configured glue package, so Cucumber never registered it. Nothing reports an error, because an unregistered hook is indistinguishable from no hook at all. Either move the class under the glue root or add its package to the option — and prefer the first, so one package tree holds all the glue.
  • Why not point the glue option at a broad parent package to be safe?
    Because the scan is recursive and takes everything it finds. A wide glue path pulls in step definitions from unrelated suites, which slows discovery, couples two teams' code, and is a common cause of a step suddenly matching more than one definition. Narrow glue is a boundary, and it should be as tight as the layout allows.
  • How do you catch a broken glue path before a nightly run does?
    Run the suite with Cucumber's dry-run option in the pipeline. Discovery and step matching still run, step bodies do not, so a glue path pointing nowhere shows up as undefined steps in seconds rather than as a failed nightly. It costs almost nothing and guards a string the compiler cannot check.

The glue option is an address book, not the code itself. Give Cucumber the wrong street and the problem is not that nobody is home; it is that it never went to the right street.

saying these in an interview costs you the question

  • Gives the glue option a source folder path
  • Thinks the glue value names the feature files
  • Treats a wall of undefined steps as missing step definitions
  • Points glue at a broad parent package for safety
  • Assumes hooks are found wherever they are written
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, 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

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

A Cucumber-JVM pack outgrew the pipeline budget — how do you choose between tag tiering, parallelism and sharding?

level: principalimportance: should knowfreq 44%

basics

~20 s

Measure per-scenario durations first, then decide what each pipeline stage must gate. Tag tiering buys speed by covering less, in-JVM parallelism buys it by demanding isolated glue, and sharding across agents buys it with infrastructure and result aggregation.

open as a page

How do Cucumber-JVM's TestNG base class and CLI runner differ from a JUnit Platform suite?

level: middleimportance: nice to knowfreq 31%

basics

~20 s

AbstractTestNGCucumberTests feeds scenarios to one inherited test method through a data provider, so results arrive as repeated invocations. The command-line runner is not a test framework at all: it returns an exit code the build must interpret.

open as a page