How should I perform integration testing on a SpringBoot library, that isn't an application - spring

First up, a disclaimer, I'm not entirely happy with the architecture of what I'm about to ask about!
We have a Spring based project that produces a library as a jar. That library includes all the usual controller/service/jpa (repositories) layers you might expect but no boot application to start it all up.
The idea being various projects within our organisation can import the jar and get instant addition of a common (HTTP) API.
Unit tests work ok.
*IT.java tests are another matter.
Run in isolation by the IDE from the test/java/ hierarchy they run ok and pass.
However when running via maven as part of the build they fail.
[ERROR] ControllerIT » IllegalState Unable to find a #SpringBootConfiguration...
We have a Boot configuration in the test hierarchy which the ITs must be using when run via the IDE, but when running the maven build it seems it can't be found to start up the application (understandably as it is in the test package tree).
The boot file we do have is intended to be no more than a test harness to run the lib, to run the ITs.
We have h2 in the Maven test scope, but it isn't wanted in the final library (that is up to the host application to provide a datasource/connection etc).
Requirement:
Start up the API library in an application for testing
Run the tests as part of Maven build
H2 should not end up in final jar
Symptoms when running mvn install
[ERROR] ControllerIT » IllegalState Unable to find a #SpringBootConfiguration...
Presumably need some config somewhere in pom? Already using package.Boot in the Springboot maven plugin config.
Maybe just need to figure out the magic config to point it at the src/test/... Boot.class rather than src/main/...Boot.class
Question(s):
Does spring have a 'correct' way to achieve what we want (I'd go with MicroServices, but not an option because ... reasons)?
How should we go about implementing ITs against a H2 driven SpringBoot application when we don't want to include the bootable class (or H2 lib) in product jar?
Should we be creating a separate Maven module just for the ITs (with a dependency on the library module)?
Abbreviated Maven pom:
<properties>
<version.spring-boot>2.1.13.RELEASE</version.spring-boot>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${version.spring-boot}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.28.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.28.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.4.0</version>
<type>pom</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<!-- other dependencies excluded for brevity -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${version.spring-boot}</version>
<configuration>
<!-- file lives in test hierarchy -->
<mainClass>org.my.packages.test.Boot</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<skip>true</skip><!-- Skipping unit tests while trying to sort ITs -->
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<failIfNoTests>true</failIfNoTests>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</execution>
</plugin>
</plugins>
</build>
The Spring Boot config:
#SpringBootApplication()
public class Boot extends SpringBootServletInitializer
{
public static void main(String[] args)
{
SpringApplication.run(Boot.class, args);
}
}
IT configuration:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ControllerIT
{
#Autowired
private TestRestTemplate restTemplate;
...
}
Number 2 lead me to looking into params of #SpringBootTest.
I ended up with ...
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = ITConfig.class)
Where ITConfig is:
#SpringBootApplication()
public class ITConfig extends SpringBootServletInitializer
{
public static void main(String[] args)
{
SpringApplication.run(org.lhasalimited.libraries.ITConfig.class, args);
}
}
That appears to be working. Thanks for the hint.

Let me propose one way of solving this issue, there are might be others:
#SpringBootTest annotation is intended to mimic the startup of the full fledged spring boot application.
It scans the packages up to the point where it finds #SpringBootConfiguration annotataion (this annotation is placed on #SpringBootApplication already) and this way it understands that the packages down to the package in which the main class is found should be scanned (to find all the beans).
Besides that, it does many other things that spring boot application does during the start: loading configurations (application.properties/yaml) obeying the conventions of spring boot application, loading auto-configurations (stuff inside spring.factories) and so forth.
Bottom line you'll have a full-fledged spring boot application (usually microservice) loaded inside the test.
So far so good, but you say that you don't really have a spring boot application. So I see three ways:
Introduce an artificial class with #SpringBootConfiguration in the src/test/java/whatever-package-close-to-root folder (note, its in 'test', not in src/main)
Use #SpringBootTest with a parameter of "Configuration". This will mean that SpringBootTest won't use all this scanning up-and-then-down process and instead you'll instruct it to use the concrete context configuration.
Do not use #SpringBootTest annotation at all, and prefer a "usual" #ContextConfiguration - yes, you won't have the power of spring boot but as I've said above you probably won't need it. In addition these tests will be way faster, especially if you'll provide many "#Configuration" classes and will load only "relevant" parts of the library during the tests.
For example, if you test the DAO, there is no point in loading web related stuff.

Related

Intellij is ignoring Maven settings [duplicate]

I am trying to run tests in Intellij which used to work earlier in spring boot 2.2.x. I recently upgraded to spring boot 2.3.9. When I try to run the test from Run Configurations, it doesn't run the test and throws the error:
'failed to resolve junit platform launcher 1.6.3 intellij'.
However if I run the test in cli, it works fine.
It turns out that, junit5-platform-launcher dependency needs to be added in order for Junit5 tests to run in IntelliJ.
https://youtrack.jetbrains.com/issue/IDEA-231927?_ga=2.5997872.2063517257.1613993298-1098513328.1597974168
https://junit.org/junit5/docs/current/user-guide/#running-tests-ide-intellij-idea
Add this dependency explicitly in pom.xml, and it will solve the issue.
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
I was facing same issue "failed to resolve junit platform launcher 1.8.1" intellij.
IntellJ version: 2021.3
I found answer here and it worked, no need to add any dependency to pom.
Go to settings >> HTTP Proxy >> choose auto-detect proxy settings
For IntelliJ Idea 2021.1, I fixed a similar problem with:
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
Maybe an even better fix is:
<dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.junit/junit-bom -->
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.7.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Found the above solution on Jetbrains issue tracker
If you have no direct internet connection but a repository manager like artifactory, idea tries to resolve junit-platform-launcher from there. Make sure u have a mirror to maven central repository (virtual repository) configured and the artifactory url to this mirror is accessible WITHOUT authentication (in the settings for the repo "Force Authentication" should be unchecked).
Check also the idea proxy settings and if needed, configure an exception for the artifactory domain.
Check your proxy settings in IntelliJ Idea settings. I turned ON the proxy and it solved the problem.
Here's the official way to do this
Maven Surefire and Maven Failsafe can run JUnit 4 based tests
alongside Jupiter tests as long as you configure test scoped
dependencies on JUnit 4 and the JUnit Vintage TestEngine
implementation similar to the following.
<!-- ... -->
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
<!-- ... -->
<dependencies>
<!-- ... -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<!-- ... -->
</dependencies>
<!-- ... -->

Two the same classes in maven dependencies. sealing violation

today I have bumped with an unusual situation..
For testing Java EE I am going to use glassfish-embedded-all.
In production I am going to use apache derby data base.
So when I wrote small test class for testing DB I got this error:
java.lang.SecurityException: sealing violation: can't seal package org.apache.derby.impl.services.locks: already loaded
My pom:
<dependencies>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.14.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbynet</artifactId>
<version>10.14.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbyclient</artifactId>
<version>10.14.2.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.glassfish.main.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>3.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
The problem is that embedded server also has derby classes.
So I have dervy classes for compile and I have derby classed for tests.
And during test It causes the conflict which I don't knwo how to solve.
Seems I need to ignore derиy classes in embedded server during test. Any ideas?
My tests:
public class JpaTest {
private static final String PERSISTENCE_UNIT_NAME = "people";
private EntityManagerFactory factory;
#Before
public void setUp() throws Exception {
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
// Error appear here
EntityManager em = factory.createEntityManager();
P.S.
Ican't undersnat how Maven shade plugins work. Seems this plugin ia aimed for final jar. Not for unit test right now...
P.S.2.
If i try to execute test in some Main method it works fine (becase there is no glassfish dependency)
If i don't remember wrong, maven compile scope is used in every phase of the lifecicle, so dependencies with compile scope are used on test phase too. The solution claims to exclude the derby dependencies from glassfish one to force to use yours.
More on, you have dependencies with explicit scope defined as compile, and others than nothing is specified...theese are the same, as maven takes as default scope compile one

spring-boot-configuration-processor is not working on maven submodule project

I have a maven multi module project with one parent and three child modules.
The application uses spring boot. In one of the child modules, I have the SpringBootApplication:
#SpringBootApplication
#EnableConfigurationProperties({AppProperties.class})
public class MainSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MainSpringBootApplication.class, args);
}
}
The App Properties are in the same module:
#Data
#ConfigurationProperties(prefix = "asdf")
public class AppProperties {
...
}
In the pom.xml of that module there is a dependency for the spring-boot-configuration-processor:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
Now the problem is, when I run mvn install on the parent project, the target/classes/META-INF/spring-configuration-metadata.json
file within this child module is not created. When I modify the pom of that child module to directly inherit from:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
and do mvn install directly on the child module, the target/classes/META-INF/spring-configuration-metadata.json file is generated.
Do you have any hints?
There are two options I am aware of.
First one is my favourite (especially because it configures the order of APT libraries - the order of code generation).
But based, say, on the IDE auto-discovery mechanism, the 2nd one is also a good bet.
Both ones are primarily targeting the minimum size of the final artefact (dependencies' scope), which, for me is very important.
Not to increase the deliverable/archetype size with useless dependencies (apt libraries are needed only at compile time) is very important in the era of k8s/docker/cloud (resource efficiency).
So, without further ado, the options:
Use APT libraries only in maven compiler plugin configuration (nothing in dependencies).
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
This option is useful for the case the maven-compiler-plugin is not configured in plugins/pluginsManagement (but probably by means of its properties).
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
Notes:
For option 2, scope provided is important, because it will allow to use the APT during the compilation but won't be included in the final artefact).
On the other hand (back to your question), in order to generate documentation in target/classes/META-INF/spring-configuration-metadata.json from the java doc, for lombok based java classes, you need these as well (#Getter and #Setter - are both needed).
#Setter
#Getter
#ConfigurationProperties(prefix = "asdf")
public class AppProperties {
/**
* foo - Should not be null or empty.
*/
private Map<String, String> foo;
and the following maven compiler plugin configuration as property (or in the plugin configuration).
<maven.compiler.parameters>true</maven.compiler.parameters>
After the compilation the IDE will parse the spring-configuration-metadata.json file and offer suggestion/quick doc/autocomplete in application.properties/application.yml.
Kr
I explicitly added:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.1.5.RELEASE</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</plugin>
to the plugins section of the pom of the child module containing the #ConfigurationProperties annotated class. Now target/classes/META-INF/spring-configuration-metadata.json is generated.

Adding Spring (Boot?) to existing RESTEasy JAX-RS application

I have an existing Maven project based on JAX-RS using RESTEasy. It works great! It creates a WAR that I deploy to Tomcat running on Ubuntu. It's clean and follows the latest standards. The POM is simple:
...
<packaging>war</packaging>
...
<dependencies>
...
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>3.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-servlet-initializer</artifactId>
<version>3.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>3.1.0.Final</version>
</dependency>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
...
I don't need any web.xml because I'm using the latest Java EE annotations:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/")
public class MyRESTApplication extends Application {
final FooResource fooResource = new FooResourceService();
...
#Override
public Set<Object> getSingletons() {
return ImmutableSet.of(fooResource);
}
}
This is all simple and it's working so great! Now I just want a way to easily change FooResource implementations based on the profile --- in other words, wire my singletons. That's where Spring comes in, right? And I've been told that Spring Boot makes Spring even more awesome, and you can use it with anything, and it gives you an actuator that allows you to gain real-time inside on the health of your system.
Unfortunately all the Spring Boot books and tutorials seem to think I'm starting with one of their quick-start applications. But I already have a great, simple application. I just want to:
Get my application wiring, based on profiles, from an external configuration file (not annotations) via Spring.
Get whatever other goodness comes from Spring Boot, because apparently it is awesome and will completely transform my application.
How do I add Spring (or Spring Boot) to this simple little JAX-RS application?
we solved it that way, that we created a singleton spring bean, let's call it ServiceStartupClass, where we register all JAX-RS services.
Here some code snippet how we start our services:
import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;
#Component
#ApplicationPath("/")
public class ServiceStartupClass extends ResourceConfig {
#PostConstruct
public void startup() {
register(FooResource.class);
...
}
}
If you need any further help, let me know

Surefire able to run test, failsafe not

I have some integration-tests that run perfectly using the surefire plugin with command:
mvn -Dtest=path.to.test.classIT surefire:test
When I run the same integrationtest with the failsafe plugin using
mvn verify
the test fails indicating it is missing a dependency (jackson lib, "No Message body writer found for response class ").
The needed dependency is added to the pom with scope test. What is the difference in how surefire and failsafe executes tests?
Some more context:
My pom contains the following:
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<forkMode>never</forkMode>
<threadCount>1</threadCount>
</configuration>
</plugin>
...
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>openejb-cxf-rs</artifactId>
<version>4.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>openejb-mockito</artifactId>
<version>4.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.2.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<scope>provided</scope>
</dependency>
The test class uses applicationcomposer
#RunWith(ApplicationComposer.class)
public class PdaServiceIT {
....
#Configuration
public Properties config() throws Exception {
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.RemoteInitialContextFactory");
properties.setProperty(OpenEjbContainer.OPENEJB_EMBEDDED_REMOTABLE, "true");
properties.setProperty("cxf.jaxrs.providers", "com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider");
return properties;
}
...
The problem might be caused because the class used as Json provider is not on the classpath. This can be solved by adding a module that contains the class to the integration test:
...
#RunWith(ApplicationComposer.class)
public class PdaServiceIT {
...
#Configuration
public Properties config() throws Exception {
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.RemoteInitialContextFactory");
properties.setProperty(OpenEjbContainer.OPENEJB_EMBEDDED_REMOTABLE, "true");
properties.setProperty("cxf.jaxrs.providers", "com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider");
return properties;
}
#Module
public static Class<?>[] myJaxbProviders() {
return new Class<?>[] { com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider.class };
}
...
}
Then the required class will be available when using both maven-surefire and maven-failsafe plugins.
A similar integration test setup is described here.
I'm not really sure about what is happening here but there is a full documentation page about Class loading issues that might be related to your problem.
https://maven.apache.org/surefire/maven-failsafe-plugin/examples/class-loading.html
This page explains that, while using the parameter forkMode=never (Which is apparently deprecated), the plugin have to use an isolated classloader.
One of the limitation of having an isolated class loader is explained here:
For example, the system property java.class.path won't include your
jars; if your app notices this, it could cause a problem
I agree that it is a bit esoteric in this case but I can't stop thinking it is related to your issue.
Did you try to change the current configuration you have? I don't know, maybe dropping the forkMode and threadCount and see how the default configuration is handling all that?
If it doesn't work, I would try to
tinker with these three settings: forkCount, useSystemClassLoader,
and useManifestOnlyJar.
As the document says.

Resources