Search code examples
springmavenspring-bootcucumber-javacucumber-junit

Glue code is not loaded when running with cucumber-spring back-end from jar file


I have been trying to get spring-based cucumber tests to run using a combination of Junit(4.12), Cucumber-Java(4.1.1), Cucumber-Spring(4.1.1) and Cucumber-Junit(4.1.1). I have no issues loading glue code when running the tests from inside the IDE (IntelliJ 2018.3.4) but it seems that for some reason when I try running from the a compiled jar file (which is a requirement in this case) cucumber doesn't find the step definitions.

I've already tried multiple glue code formats such as: "classpath:com.a.b.c.stepdefs" "com.a.b.c.stepdefs" "classpath:com/a/b/c/stepdefs"

I've also tried providing relative paths from the runner class up to the step definitions class (nested just one level below) "stepdefs"

Also gave a try running using both JUnit and the cucumber.cli.Main and attempted to use different style of step definitions (both cucumber expression - which the missing step snippets are pointing me to - and regex)

I am using the spring-boot-maven-plugin so I am aware that that generally changes the jar structure

All of the above variations fully work when running from the IDE, but not from the jar file

Main Class:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
@ComponentScan(basePackages = {"com.a.b.test.core.data",
    "com.a.b.c",
    "com.a.b.c.stepdefs"}
)
public class CucumberApplication {

    public static void main(String[] args) throws IOException, InterruptedException {

        SpringApplication.run(CucumberApplication.class, args);

        Result result = JUnitCore.runClasses(RunnerCentral.class);
        System.exit(result.wasSuccessful() ? 0 : 1);

    }
}

Runner Class:

package com.a.b.c;

@RunWith(Cucumber.class)
@CucumberOptions(features = "classpath:BOOT-INF/classes/features",
glue = "classpath:com/a/b/c/stepdefs",
plugin = "json:target/cucumber-html-reports/cucumber.json")
public class RunnerCentral {
}

POM config of spring-boot-maven-plugin:

  <plugin>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-maven-plugin</artifactId>
     <version>2.1.0.RELEASE</version>
         <configuration>
             <fork>true</fork>
             <mainClass>${start-class}</mainClass>
             <requiresUnpack>
                 <dependency>
                     <groupId>io.cucumber</groupId>
                     <artifactId>cucumber-java</artifactId>
                 </dependency>
                 <dependency>
                     <groupId>io.cucumber</groupId>
                     <artifactId>cucumber-spring</artifactId>
                 </dependency>
                 <dependency>
                     <groupId>io.cucumber</groupId>
                     <artifactId>cucumber-junit</artifactId>
                 </dependency>
             </requiresUnpack>
         </configuration>
         <executions>
             <execution>
                 <goals>
                     <goal>repackage</goal>
                 </goals>
             </execution>
         </executions>
     </plugin> 

I am expecting the behavior to be consistent between running from IDE and running from a packaged source although I may be missing something

Another thing I want to mention is that when swapping the backend with cucumber-picocontainer everything seems to work (spring is a requirement so a swap isn't possible)


Solution

  • This is the kind of issue that can have you launching your hot coffee at the nearest colleague.

    Have you seen this post about using a custom ResourceLoader https://github.com/cucumber/cucumber-jvm/issues/1320

    I think you'd have to copy and paste the Cucumber.java class, providing the resource loader to the runtime from the Application Context, and change your RunnerCentral class to RunWith the new class.

    FWIW in my case, I placed the raw project in a docker container, that on startup ran ./mvnw test which is the Maven Wrapper supplied in Spring Boot projects. You can do ./mvnw test -s /path/to/maven/settings.xml if using a corporate repository, and if your container host can't access the corporate repository, run the image first on the Jenkins box (or wherever the image is being built) which will cause the dependency jars to be downloaded inside, then commit the docker image, and push that image out.

    That way, the container can run the cucumber test phase using the local .m2 directory inside it, with the dependencies it needs already there.