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

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)

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.

Related

Auto-Restart for Spring Boot Tests

I am currently writing unit and integration tests for a Spring Boot application. I'm using Spring Tool Suites 4 for development.
When I run the application using Spring Tool Suites, the auto-restart works fine when I modify and save a file. I'm trying to find a similar way to run my tests.
I currently run the tests using a separate Windows CMD terminal using Maven:
mvn test
This runs one time and terminates. Is there anyway to have the tests run every time a test file is saved?
Edit: Here's an example of a test I am running that uses JUnit and Spring to run the tests. This is taken straight from the Spring.io website https://spring.io/guides/gs/testing-web/
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
public class ControllerTest {
#Autowired
private Controller controller;
#Test
public void contexLoads() throws Exception {
assertThat(controller).isNotNull();
}
}
I found this Maven plugin https://github.com/fizzed/maven-plugins#watcher-fizzed-watcher-maven-plugin that will watch files and let you run specific Maven goals on file changes.
I added the plugin to my POM and changed the goal to "test".
<build>
<plugins>
<plugin>
<groupId>com.fizzed</groupId>
<artifactId>fizzed-watcher-maven-plugin</artifactId>
<version>1.0.6</version>
<configuration>
<touchFile>target/classes/watcher.txt</touchFile>
<watches>
<watch>
<directory>src/main/java</directory>
</watch>
<watch>
<directory>src/test/java</directory>
</watch>
</watches>
<goals>
<goal>test</goal>
</goals>
</configuration>
</plugin>
<plugins>
<build>
I opened a terminal within Spring Tool Suites (ctrl + alt + t) and docked it next to my console and ran the following:
mvn fizzed-watcher:run
So far this seems to be working exactly like I want.

Run nested JUnit 5 tests with Maven Surefire

I'm trying to use JUnit 5 in my side project as a trial before migrating my main project. I'd like to use #Nested tests to make my test classes cleaner.
Everything is fine when I ran my test suite as a whole. However, as soon as I try running just a single test, #Nested ones are not executed.
mvn -Dtest=com.mycompany.test.MyTest surefire:test
Is there any way of getting it to run the selected class and all #Nested ones?
Using JUnit 5.1.0, JUnit platform 1.1.0
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>${org.junit.platform.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${org.junit.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
Test class:
public class MyTest {
#Test
public void thisTestExecutes() { }
#Nested
public class NestedTests {
#Test
public void thisTestDoesnt() { }
}
}
To run all nested classes you just need to add an "*" at end of class name. Something like:
mvn -Dtest=com.mycompany.test.MyTest\* surefire:test
Facing with same issue, then realized my parent test class name and .java file name are different. I changed my test class name to my .java file name with right click > Refactor > Rename (for possible reference issue). Lastly run my test with following command:
mvn -Dtest=com.mycompany.test.MyTest*
By the way I'm using maven-surefire-plugin 2.22.2 version.
Whole problem is that nested tests are classes compiled the same way as the anonymous classes with a name containing $. The Surefire and Failsafe excludes these by the default pattern
**/*$*
If you use lambda then these exclusions become more and more important.
This should work as well:
mvn test -Dexcludes=nonetest

Run a Maven plugin when the build fails

I am using a plugin to send a Slack message through Maven. I am wondering if it's possible to use a plugin when the build failed so I get automatically notified about the failed build?
You could do that within Maven itself, through the EventSpy mechanism, built-in from Maven 3.0.2. At each step of the build, several events are raised by Maven itself, or by custom code, and it is possible to listen to those events to perform some actions. The execution event raised by Maven are represented by the class ExecutionEvent. Each event has a type, that describes what kind of event it represents: project failure, Mojo failure, project skipped, etc. In this case, the project failure event is what you're looking for.
A custom spy on events is just a Java class that implements the EventSpy interface. Preferably, it should inherit from the AbstractEventSpy helper class. As an example, create a new project (let's call it my-spy), and add the following Java class under a package:
import org.apache.maven.eventspy.AbstractEventSpy;
import org.apache.maven.eventspy.EventSpy;
import org.apache.maven.execution.ExecutionEvent;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
#Component(role = EventSpy.class)
public class BuildFailureEventSpy extends AbstractEventSpy {
#Requirement
private Logger logger;
#Override
public void onEvent(Object event) throws Exception {
if (event instanceof ExecutionEvent) {
ExecutionEvent executionEvent = (ExecutionEvent) event;
if (executionEvent.getType() == ExecutionEvent.Type.ProjectFailed) {
logger.info("My spy detected a build failure, do the necessary here!");
}
}
}
}
This code simply registers the spy through the Plexus' #Component annotation, and logs a message when a project failed to build. To compile that class, you just need to add to the my-spy project a dependency on Maven Core and an execution of the plexus-component-metadata plugin to create the right Plexus metadata for the component.
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.0.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-metadata</artifactId>
<version>1.6</version>
<executions>
<execution>
<goals>
<goal>generate-metadata</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Once this project is compiled and installed into your local repository (through mvn clean install), you can add it to the build of another project through the core extensions mechanism.
Before Maven 3.3.1, you had to drop the my-spy JAR into your ${MAVEN_HOME}/lib/ext folder, so that Maven could find it. As of 3.3.1, you don't need to fiddle with your Maven installation, and can create a file .mvn/extensions.xml in your project base directory (${maven.multiModuleProjectDirectory}/.mvn/extensions.xml). Its content would be
<?xml version="1.0" encoding="UTF-8"?>
<extensions>
<extension>
<groupId>my.spy</groupId>
<artifactId>my-spy</artifactId>
<version>0.0.1</version>
</extension>
</extensions>
which just declares an extension pointing to the Maven coordinates of the spy project. Maven (≥ 3.3.1) will by default look for that file, and, as such, your spy will be correctly registered and invoked throughout the build.
The only remaining thing to do, is to code what the spy should do. In your case, it should invoke a Maven plugin, so you take a look at the Mojo Executor library, which makes that very easy to do.

Turn off or configure java.util.Logging during Maven build

I want to clean up/unify the log output of a Maven build. Unfortunately, a dependency of a maven plugin uses java.util.Logging (JUL). Simply adding org.slf4j:jul-to-slf4j as an additional dependency to redirect the log output doesn't help. Exclusions also don't work of course, because it's JUL and therefore not a dependency.
Here's my configuration, containing the specific plugin and its dependency:
Configuration
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>2.7.11</version>
<executions>...</executions>
<configuration>
<defaultOptions>
<extraargs>
<extraarg>-xjc-Xbg</extraarg>
</extraargs>
</defaultOptions>
</configuration>
<dependencies>
<!-- This one uses JUL with logging level WARN -->
<dependency>
<groupId>org.apache.cxf.xjcplugins</groupId>
<artifactId>cxf-xjc-boolean</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
</plugin>
Update
I've managed to turn off/configure JUL itself by adding a logging.properties file with the following content to the project's root folder:
# Turn off any java.util.Loggers (or use SEVERE to see problems)
.level=OFF
Additionally, I have to execute Maven with the following parameter (or set it into MAVEN_OPTS):
mvn clean install -Djava.util.logging.config.file=${basedir}/logging.properties
Remaining Question
Is there any way to achieve the same result without extra files and/or JVM parameters (pom.xml only)?
Following configuration for the plugin cxf-codegen-plugin prevented to show DEBUG log entries when building my project with maven:
<configuration>
<fork>once</fork>
<additionalJvmArgs>
-Dorg.apache.cxf.Logger=null
</additionalJvmArgs>
</configuration>
Instead of a properties file or completely disabling all logging as suggested in another answer, you can also set the desired log level like this:
<additionalJvmArgs>-Dorg.apache.cxf.level=OFF</additionalJvmArgs>
The level needs to be one of the java.util.logging.Level.
I was having similar behavior: was getting WARNING messages from CXF 2.6.3 generated webservices classes when running my Struts2 test cases, polluting my test log.
Since the log trace wasn't showing from what package was sended, I was trying to config the wrong package.
I've added this to my setUp() test case method from my test that extends XWorkTestCase (or whatever you need):
public void setUp() throws Exception{
super.setUp();
java.util.logging.Logger.getLogger("org.apache.cxf").setLevel(Level.OFF);
}
Then no more logging messages from CXF appears when I run my tests from maven.

Configure Maven to use CXF wsdl2java with Basic Authentication

I have an application that needs to integrate with one of SharePoint's web services. This web service cannot be accessed freely and needs authentication.
As such, the standard wsdl2java Maven plugin in my application gives an HTTP 401 error when the generate-sources phase is executed.
Is there a way to setup Maven/POM so that I can provide a user/password that will generate the stubs?
I have come across some answers saying this is not possible but all answers are older than 1 year. I haven't found if Maven have issued an update on this. One option is to save a local copy of the WSDL (as suggested here) but I would like to avoid having local copies.
Because you mentioned CXF then I suppose you meant cxf-codegen-plugin. It's a bit of a hack but it works.
HTTP authentication credentials can be provided using java.net.Authenticator. One need to just define his own Authenticator class which overrides getPasswordAuthentication(..) method. Then it has to be set as default Authenticator. As far as I know it can't be done declaratively (for instance using environment properties) only programatically using Authenticator.setDefault(..).
In order to call Authenticator.setDefault(..) I would use CXF extension mechanism. Create separate maven project with similar class:
public class AuthenticatorReplacer {
public AuthenticatorReplacer(Bus bus) {
java.net.Authenticator.setDefault(new java.net.Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("test", "test123"
.toCharArray());
}
});
}
}
and file src\main\resources\META-INF\cxf\bus-extensions.txt with contents:
org.example.AuthenticatorReplacer::false
Then add newly created project as a dependency to cxf-codegen-plugin:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${project.version}</version>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>cxf-authenticator-replacer</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
...
</plugin>
This way AuthenticatorReplacer is initialized by CXF extension mechanism and replaces default Authenticator with ours.
An clean alternative to #Dawid Pytel's solution would be to run this class during lifecycle of wsdl class auto generation:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>path.to.AuthenticatorReplacer</mainClass>
</configuration>
</plugin>
Important: your AuthenticatorReplacer has to be a main(String[] args) class and running the code inside.
I verified that Dawid's solution works. Alternatively, you can use SoapUI to pull down and cache the wsdl and then use SoapUi code generation support to use cxf to generate the code.
http://java.dzone.com/tips/generating-client-java-code
Dawid's solution works for me too. It is a little tricky though. In Eclipse, the pom.xml keeps complaining that "wsdl2java failed: Could not load extension class AuthenticatorReplacer". You have to ignore this error message and use the command line:
mvn generate-sources
The Java classes will then be generated successfully.

Resources