Spring Integration Testing does not pick up JPA beans from other modules - java

I have a multi module maven project. In one module I have all my persistence stuff like entities and repositories and in another module I import that persistence module and I want to run some integration tests.
The problem is that my tests do not pick up repositories and can't create the context because of the missing beans. Can someone help me with what I am missing?
The project structure looks like this
persistence module
business logic module
web module
In my business logic module pom I import the persistence module
<dependency>
<groupId>com.acme</groupId>
<artifactId>persistence</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
My integration tests configuration
#Configuration
#ComponentScan({"com.acme.persistence", "com.acme.business"})
#EnableAutoConfiguration
public class DataTestConfig {
}
and all my tests are annotated with the following
#SpringJUnitConfig(DataTestConfig.class)
#DataJpaTest
If I move all the persistence module inside the business module then the tests work fine, but I want to keep them in separate modules.
Thanks

I found the solution.
I added #EnableJpaRepositories on my test configuration class.
This way repositories were created and now my services that used repositories can be instantiated.
This is how the final integration testing configuration looks like
#Configuration
#ComponentScan({"com.acme.persistence", "com.acme.business"})
#EntityScan("com.acme.persistence")
#EnableJpaRepositories(basePackages = "com.acme.persistence")
#EnableAutoConfiguration
public class DataTestConfig {
}

Related

Spring boot custom starter - Cannot import custom starter class

I am developing a spring boot custom starter which pom contains some dependencies (other starters, libraries) and this starter does some config about jwt filtering to allow the filter at security level. The issue is when I add my custom starter as a pom dependency in another project (starter-consumer), it seems it detects the class I want to import but IntelliJ does nothing.
Maybe I didn't packaged the starter correctly, or at least the classes that I coded inside. The other dependencies that the starter contains in the pom are successfully added as a dependencies of the starter-consumer. For example, I use some jwt utils which dependency is in the parent starter. So that thing works ok.
import io.jsonwebtoken.impl.DefaultClaims;
The problem is when I try to import a custom class which I coded in the starter. This is my package structure:
I want to use the JwtConfig class in my starter-consumer. It appears but I can't import. It does nothing.
And then If I manually check package availability I see this:
Pepito is missing :( and theinit is the package name of the starter-consumer. The jar is installed in the local m2 so I get the dependency like this:
<dependency>
<groupId>com.pepito</groupId>
<artifactId>starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
Any insights on this?
Edit 1:
I removed the boot maven plugin as a you said, it seems now it is not packaged as boot app and my starter-consumer can import the clases I coded . One additional thing, what happens with beans? Does the autoconfigure starts the bean by itself or the starter-consumer needs to declare it?
Edit 2:
So as part of the solution of this post, I am trying to inject a bean from the starter into the starter-consumer.
Apart from another beans, here we have the jwtTokenAuthenticationFilter which I want to inject into my starter-consumer security config.
#Configuration
#ConditionalOnProperty(name = "security.jwt-enabled", havingValue = "true")
public class JwtAutoConfiguration extends AbstractHttpConfigurer<JwtAutoConfiguration, HttpSecurity> {
#Bean
public JwtConfig jwtConfig() {
return new JwtConfig();
}
#Bean
public FilterRegistrationBean registration(#Qualifier("jwtFilter") JwtTokenAuthenticationFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
#Bean
public JwtTokenAuthenticationFilter jwtFilter() {
return new JwtTokenAuthenticationFilter(jwtConfig());
}
#Override
public void init(HttpSecurity http) throws Exception {
// initialization code
}
}
This is my spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pepito.starter.configuration.security.jwt.JwtAutoConfiguration
And in the starter-consumer I have the following
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtTokenAuthenticationFilter jwtFilter;
And here is where I see the error in intellij that can't autowire that bean because it does not exist. I suppose is because of something about scan but in my starter-consumer I have #SpringBootApplication which is suppose it contains componentscan annotation.
I believe A couple of issues that I see here and some clarifications will help you to find an answer:
Module with starter is a regular jar. The only thing that differs is that it has META-INF/spring.factories which I see exist.
Having said that - I see in the Pepito starter module class SpringBootMicroserviceStarterApplication. This is wrong. If the starter is a spring boot application, then the chances are that you're using spring boot maven plugin to prepare it as an application.
But the jar created by this plugin is not really a Jar, more specifically it stores other jars in BOOT-INF lib and in general can't be considered a Jar build-tool-wise and IDE-wise.
To put it simple you cannot declare a dependency on something that gets packaged as a spring boot application!
Update 1
To address your OP's question in comments:
The starter is not a spring boot application. It should not have a method annotated with #SpringBootApplication, it should not be packaged as spring boot application and so forth.
The best way to view the starter is as an self-contained "feature" or "facility" (trying to find the most appropriate word in English) that can be used by Spring boot applications by importing the starter module.
In terms of testing, #SpringBootTest should not be used in starter's module because it mimics the startup of spring boot application which obviously does not exist here.
Its possible to test it with Unit Test or with Spring Testing Framework:
#RunWith(SpringRunner.class)
#ContextConfiguration(<here comes the class of AutoConfiguration in starter)
One last thing to clarify here:
In spring Factories you can specify the autoconfiguration that in turn declares a series of beans required by the starter.
These beans are resolved by spring boot application just like other beans, the difference is that the Configuration is identified out of spring.factories file and not by package structure or explicit configuration.
So to answer your question:
Would you know if I can declare a bean in the starter and then autowire it in the consumer?
Yes, as long as the starter gets loaded by spring boot application, you can autowire (or inject in any other way) beans from starter into the beans of the spring boot application itself.

Spring Boot Autowiring of beans is not working in maven multi module project

While modularising our project into different independent maven projects using spring boot and maven, we have came across a issue where autowiring of beans in multi module maven project is not working.
Just to give you an overview of the issue, below are the independent maven projects developed so far
Coreservices – Contains spring boot domain objects of whole application : Output JAR
DBservices1-Contains spring boot repositories and services(Database Services) to access database : Output JAR
Rewards -Contains Rewards module related files(Controllers, services(Business Logic Services), Views) : Output JAR
RewardsApp- Independent deployable maven project : Output WAR
Below is the dependency structure
RewardsApp-> Rewards -> DBservices1 -> Coreservices
The problem is #Autowired annotation used in Rewards and DBservices1 to fetch the mapped services annotated with #Service/#Repository are not available in RewardsApp Project.
As a workaround we have configured the beans in RewardsApp with #Bean annotation, then the services are available to the server to start successfully.
With this approach we need to manually configure all the beans in RewardsApp used in dependent projects.
We have many services and repositories in our application and we think creating beans like this not a proper way as many beans need to be created.
Please note that we have created all the spring boot controllers,services,repositorys across all projects under
package com.company.application
Below is the snippet of main class:
#SpringBootApplication
#ComponentScan(basePackages = {"com.company.application"})
public class RewardsApp extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(RewardsApp.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(RewardsApp.class);
}
}
/**
*Manual beans in RewardsApp
**/
#Bean
public SomeService someService()
{
return new SomeService();
}
By adding below annotation in RewardsApp.java did the trick for me, now autowiring was working for the classes inside the jars
#ComponentScan(basePackages = {"com.company"})
#EntityScan(basePackages = {"com.company"})
#EnableJpaRepositories(basePackages = {"com.company"})
I guess above are for Services,Entities(Domains),Repositories
How about having a configuration class (with relevant comp scans) for each module and importing those configs into your application class?
#SpringBootApplication
#ComponentScan(...)
#Import({RewardsContext.class, DBservicesContext.class})
...
Import docs here

Sharing common Spring Boot configuration between projects

The app I'm working on has a maven dependency on a common module containing a dozen spring-boot #Configuration beans specifying datasources, LDAP contexts, security modules, property sources etc which I often want to suppress.
My app and this common module are part of a spring-boot maven multi-module project. My sub-project needs about 6 of the 12 configuration beans.
I have got quite a long way using #Import and #SpringBootApplication#exclude and #ImportAutoConfiguration:
#Import({PropertySpringConfig.class,
LdapConfig.class,
SecurityConfig.class,
JpaDataConfiguration.class,
RestConfiguration.class,
WebConfigurer.class})
#SpringBootApplication(exclude = {
RefDataSourceConfig.class,
ElasticsearchAutoConfiguration.class,
CamelAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class})
public class MyRestApplication {
public static void main(String[] args) {
SpringApplication.run(MyRestApplication.class, args);
}
}
and a test configuration bean that all my JPA tests import (I have others, e.g. for REST tests):
#Configuration
#OverrideAutoConfiguration(enabled = false)
#ImportAutoConfiguration(value = {
CacheAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
TransactionAutoConfiguration.class,
TestDatabaseAutoConfiguration.class,
TestEntityManagerAutoConfiguration.class })
public class TestJpaConfiguration {}
The whole codebase was set up with Spring 1.3.x. I upgraded it to Spring 1.4.x.
Take for example one of the datasources, a configuration bean in the shared dependency - I don't need it, and it prevents Spring Boot autoconfiguration because it's marked with #Primary (possibly unnecessarily).
I don't want Spring Boot in my sub-project to see it when it runs autoconfiguration, but how do I share it from the common module with the other Spring Boot sub-projects that do need it?
I could split it out into its own maven project and only have it as a dependency in the sub-projects that needed it. But there are 11 other similar configuration beans! It could be seen as overkill although I like this approach - but I have 5 colleauges to convince.
I can just struggle on using #SpringBootApplication#excludes for the code and #ImportAutoConfiguration for the tests - but then I miss out on the Spring Boot benefits like #DataJpaTest or #JsonTest test slices
I could repeat the configuration beans in each project where they are needed - a bit of cut & paste - but I like this option the least.
Is there a 4?

How to test Maven module project with Spring Boot

I have split a project, based on Spring Boot, into several Maven modules. Now only the war-project contains a starter class (having a main method, starting Spring), the other modules are of type jar.
How do I test the jar projects, if they don't include a starter?
Example JUnit test case header:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(StarterClassInDifferentProject.class)
...
I think context tests should be available per module so you can find issues with wire and configuration early on and not depend on your full application tests to find them.
I worked around this issue with a test application class in the same module.
Make sure this main class is in your test dir.
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
your context should work now.
#RunWith(SpringRunner.class)
#ActiveProfiles(profiles = {Profiles.WEB_REST})
#WebMvcTest(EntityController.class)
#DirtiesContext
public class ServicesControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private Controller controller;
#Test
public void testAll() throws Exception {
given(controller.process(null)).willReturn(null);
mvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
I solved a similar situation.
I have a project with two modules:
a "lib" project with domain and utilities classes,
a "web" projects with a spring boot application, templates, controllers, etc...
and I wanted to test the "lib" project in a spring-boot-test fashion.
First, include the required dependencies with scope "test" in the pom.xml (in my case there is also the H2 database):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>1.3.3.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- add also add this here, even if in my project it is already present as a regular dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.3.3.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.191</version>
<scope>test</scope>
</dependency>
For testing purposes, among the test sources of the "lib" project, I have a class that acts as my test configuration
package my.pack.utils;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#TestConfiguration
#EnableJpaRepositories(basePackages = {"my.pack.engine.storage", "my.pack.storage"})
#EntityScan(basePackages = {"my.pack.storage", "my.pack.entity"})
#EnableAutoConfiguration
public class MyTestConfiguration
{
}
This sets up the H2 database in order to test the data access functionalities of the application
Finally, only in the test classes where I find it useful, I configure the execution to use the test configuration (I do not always need to do that, but sometimes it is handy):
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyTestConfiguration.class)
public class TestAClassThatNeedsSpringRepositories
{
// tests...
}
The question is
How do I test the jar projects, if they don't include a starter?
I believe the right answer, is that your jar submodules should not be united tested with spring-boot context.
In fact, most if not all tests in your jar projects should not even use the RunWith(Spring...)
They should be vanilla or using a mock library such as #RunWith(MockitoJUnitRunner.class).
If you read SpringApplicationConfiguration's javadoc:
Class-level annotation that is used to determine how to load and configure an ApplicationContext for integration tests.
It is considered integration testing.
Other than that, you can also launch your tests using spring context (not spring-boot) with a 'test spring configuration' in your jar submodule. Define your beans/resources and use it in your test.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(TestConfigInJarModule.class)
For instance, I do this to test Spring data Repositories, using a test spring configuration (without dependencies on spring-boot).

Spring Test. Get access of test class of dependent project?

I have multi module projects; I am creating Junit Test classes for testing purpose.
But the problem is, when I run my Core Project Test using only 2 package ComponentScan for testing. It is not able to locate Util Project Test config.(Test package get excluded while building JAR)
Because of this, I am getting, No qualifying bean of type exception as Configuration class is missing from Util project.
#SpringBootApplication
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = { CreateProfileTest.class })
#ComponentScan({"com.myproject.testconfig","com.myproject.module"})
#EnableAutoConfiguration
public class CreateProfileTest {
#Test
public void myMethod()
{
}
}
So, How I can gain access to Util Project Test Config in Core Project Test.
Job - > Core -> Util (Here Core is depend on Util)
Now
CoreProject/
src/main/com.myproject.config
src/main/com.myproject.module
src/test/com.myproject.testconfig
UtilPorject/
src/main/com.myproject.config
src/main/com.myproject.module
src/test/com.myproject.testconfig
Please let me know, If there is any alternative way to achieve this or if I am doing anything wrong here.
I am using Maven and Spring Boot.
It seems to me this is a matter of classpath. Have you tried adding this in the CoreProject pom.xml ?
<dependency>
<groupId>...</groupId>
<artifactId>util-project</artifactId>
<version>...</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>

Categories

Resources