Cucumber glue-path resolution discrepancy in packaged JAR artifacts
0 reputation · 19 Jun 2024, 06:16 UTC
0 reputation · 19 Jun 2024, 06:16 UTC
Cucumber test suites execute successfully in a local environment but fail with 'undefined steps' when run within a production CI pipeline. While the local environment correctly identifies step definitions from the src/test/java directory, the production environment fails to locate these classes after they are packaged into a JAR.
The issue appears linked to how Cucumber JVM 7.x handles the glue path when scanning for classes with @CucumberContextConfiguration. If the step definitions are nested within a specific package structure or the working directory in the container differs from the local IDE, the runner may fail to map the feature files to the corresponding code.
How does Cucumber resolve glue-path patterns differently when the step definitions are located inside a compressed JAR versus a flat file system? Are there specific configuration properties required to ensure the scanner traverses the classpath across different working directories in Docker-based production environments?
34275 reputation · 19 Jun 2024, 17:18 UTC
Cucumber JVM 7.x resolves glue paths by scanning the classpath for classes in the packages you declare via cucumber.glue. It does not scan the filesystem. When tests run from source (IDE, mvn test, gradle test), the classpath contains an exploded target/test-classes directory. When the same tests run from a packaged test-JAR in CI, the classes live inside a ZIP entry. The scanner uses ClassLoader.getResources("com/example/steps"), which behaves differently across classloaders (URLClassLoader, Spring's InstrumentableClassLoader, modular runtimes). If cucumber.glue is omitted, Cucumber falls back to the runner's package—often only the runner class itself—so step definitions in a different package (or module) become invisible inside the JAR.
cucumber.glue accepts Java package names (e.g., com.example.stepdefs), never filesystem paths.com/example/stepdefs) and queries the context classloader.cucumber.glue.file: feature-file resolution; use classpath: prefixes or absolute file: URLs.cucumber.properties, junit-platform.properties, and -Dcucumber.glue=... for deterministic configuration.Your runner class sits in a package that does not share a prefix with the step definitions (e.g., runner in com.example.runner, steps in com.example.steps). Locally, the exploded directory tree lets the fallback scanner stumble into sibling packages. Inside the JAR, ClassLoader.getResources() returns only entries under the runner's package, so zero step definitions are found.
src/test/resources/cucumber.properties: cucumber.glue=com.example.stepdefssrc/test/resources/junit-platform.properties: cucumber.glue=com.example.stepdefs-Dcucumber.glue=com.example.stepdefscucumber.glue=com.example.steps,com.example.hooks.jar tf target/*test*.jar | grep -E 'stepdefs|StepDef'cucumber.features=classpath:features (or --features classpath:features).mvn test -Dcucumber.glue=com.example.stepdefsjava -cp "target/test-jar.jar:$(mvn dependency:build-classpath -q -DincludeScope=test -Dmdep.outputFile=/dev/stdout)" \
io.cucumber.core.cli.Main --glue com.example.stepdefs --dry-run classpath:features-Dcucumber.logger=DEBUG or a logback.xml with <logger name="io.cucumber" level="DEBUG"/>.Does your project use a multi-module layout where step definitions reside in a separate module that produces a test-JAR? If yes, confirm that the consuming module declares that test-JAR as a test scope dependency and that the glue package matches the packaged package name (not a shaded/relocated one).
Use comments to ask for clarification. Post a solution as an answer.
34,275 reputation · 19 Jun 2024, 06:57 UTC
When you run tests from source, ClassLoader.getResources("com/example/steps") returns the exploded target/test-classes directory. Inside a JAR the same call enumerates entries via the JAR URL, so the step classes must be present as resources in that archive. If the runner’s package does not share a common prefix with the step package, Cucumber’s default root scan will stop at the runner’s package and ignore the JAR entries.
test‑jar (e.g., mvn test-jar) and copy it into the image.-cp test‑jar.jar:app.jar or add the test‑jar to java -classpath so the classloader sees the step resources.-Dcucumber.glue=com.example.steps or @CucumberOptions(glue = "com.example.steps") to bypass the default root scan.cucumber.properties file in the image root with cucumber.glue=com.example.steps for deterministic configuration.With the test‑jar on the classpath and the glue package declared, Cucumber will discover step definitions regardless of the container’s working directory.