Problems with Testing Spring Boot Application with Multi Maven Project Setup - java

I am currently running into some problems with spring boot and multi maven project structure. I am using Spring Boot 4.3.1.
My project structure looks as follows:
parent
-- pom.xml
-- application
-- pom.xml
-- src
-- main
-- java
-- Application.java (annotated with #SpringBootApplication)
-- test
-- java
-- MyApplicationTest.java (annotated with #SpringBootTest)
-- library
-- pom.xml
-- src
-- main
-- java (...)
-- test
-- java
-- MyLibraryTest.java (annotated with #SpringBootTest)
application module has a dependency on library.
MyApplicationTest works perfectly fine, but running MyLibraryTest instead, I fail with the following error:
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test at org.springframework.util.Assert.state(Assert.java:392)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.getOr FindConfigurationClasses(SpringBootTestContextBootstrapper.java:173)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.processMergedContextConfiguration(SpringBootTestContextBootstrapper.java:133)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:409)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:305)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildTestContext(AbstractTestContextBootstrapper.java:112)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.buildTestContext(SpringBootTestContextBootstrapper.java:78)
at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:120)
at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:105)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTestContextManager(SpringJUnit4ClassRunner.java:152)
My first guess is that library needs a dependency on application, but this causes a cycle.
Is there any solution to that problem?
How can I structure my application correctly?
thanks a lot for suggestions.
MyLibraryTest looks as follow:
#RunWith(SpringRunner.class)
#SpringBootTest
#Transactional
public class MyLibraryTest {
#Autowired
private MyService service;
#Test
public void testMyService_Save() {...}
}

You need to make sure that the library module's pom.xml includes -
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
which is required for the module to use the #SpringBootTest annotation. You might already be using the same in your app module but is not included in the library module.
Well post the Edit, found the question to be possibly a duplicate of Unable to find a #SpringBootConfiguration when doing a JpaTest
Also quoting from Thomas's answer from the same thread, here
The thing about #DataJpaTest and a few other annotations is that they
look for a #SpringBootConfiguration annotation in the current package,
and if they cannot find it there, they traverse the package hierarchy
until they find it.
For example, if the fully qualified name for your test class was
com.example.test.JpaTest and the one for your application was
com.example.Application, then your test class would be able to find
the #SpringBootApplication (and therein, the
#SpringBootConfiguration).
If the application resided in a different branch of the package
hierarchy, however, like com.example.application.Application, it would
not find it.
which seems to be the case for you, where you are trying to test an application in a different module itself. Hence the error that you see.

Related

Spring-Boot Components from other jar are not in context

I have following situation. JDK 17, Spring-Boot: 2.6.2. A gradle multi-project. One project is a library (java-library, no spring boot plugin). Another project is a spring boot application with spring boot plugin. Generally, the spring dependency management plugin is not used, gradle platform concept is used instead. Application project includes dependency to library project per "implementation(project(':.."
Library:
Library project and spring boot application project have different packages.
like:
library root package is a.b.c and application root package is a.b.d
Library project has in root package of the package hierarchy a configuration class (for example a.b.c.LibraryConfig) which is annotated with #Configuration annotation and with #ComponentScan annotation, which has attribute "basePackageClass" pointing to this configuration class:
#Configuration
#ComponentScan(basePackageClass = LibraryConfig.class)
public class LibraryConfig {
Inside of library hierarchy (so in packages a.b.c.*) are services and rest controllers.
Application:
Application has in root package of the package hierarchy (for example in a.b.d) application class and in sub-package "config" it has configuration class (would be for example a.b.d.config.AppConfig), which imports the configuration class from library:
#Configuration
#Import(a.b.c.LibraryConfig.class)
public class AppConfig {
Problem:
Classes from the library are there in runtime - we are able to load them.
No any component of the library is in context (not registered), neither services nor rest controllers.
What are we missing?
We have tried different constellations of imports and scans. Also added component scan to application class and set there particular packages. Nothing helped.
The problem was, that the main application was using spring boot indexing annotation processor and library - not. So after enabling the annotation processor for library problem was solved.

Spring Boot "Ignored because not a concrete top-level class"

I have this (https://github.com/Danix43/HerculesAPI) REST application made by Java with Spring Boot.
All good when for some reason the #SpringBootApplication annotation doesn't find anymore the interfaces TermometruService annothed with #Service.
I have tried everything from using #ComponentScan with the service package but then any Rest operation returns a RESOURCE_NOT_FOUND to changing the jdk version and a flush of maven repositories.
Project structure if useful
The log after running the application
2020-01-27 19:33:39.635 DEBUG 6248 --- [main] o.s.c.a.ClassPathBeanDefinitionScanner : Ignored because not a concrete top-level class: file [D:\Programe\Programare\Java\HerculesAPI\target\classes\com\herculesapi\services\TermometruService.class]
The problem was solved by annotating the TermometruServiceImple class with #Service. Also regarding the Github repository, it doesn't always have all the changes right away pushed but yes the TermometruService interface had the annotation.

How to do component scanning for sibling package in Spring Boot

Currently I have quite a bit of services written in Spring Boot 2.1.3 (in mono-repo) and I have a common package that's used in most of the services.
So my packages are organized in the following:
root
root.common
root.serviceA
root.serviceA.<subpackages>
root.serviceB
root.serviceB.<subpackages>
...
root.serviceX
root.serviceX.<subpackages>
In each service I have Program.java where it's tagged with #SpringBootApplication (e.g. root/serviceA/Program.java)
The problem now I'm having is that I have a component in root.common (e.g. root/common/JSONSerializer.java) which needs to be loaded in all the services. I tried tagging the file with #JsonComponent but obviously it doesn't work because it's outside root.service* package.
I managed to get it working by manually adding #Import tag in the main file but that means that for each service I have to manually add #Import({root.common.JSONSerializer.class}) which is tedious and error prone. Is there a way to include this file in the component scanning process?
You can set each Program class by following way:
#SpringBootApplication(scanBasePackages = {"root.serviceA", "root.common"})
REFERENCES
SpringBootApplication documentation

Spring Maven project structure pom.xml

I'd like to get help in setting up a multi-module Maven project using Spring Boot.
Correct me if I'm wrong but I've read that Spring-Boot reads the start main Application (Annotated with #SpringBootApplication and ran with SpringApplication.run) and finds the necessary classes through reflection. Which means that it first accesses the start class and then proceeds to find the controllers, models, repositories. If so, how do I set up the dependency in the pom.xml of each module if I had a project structure like this:
app
--src
--pom.xml
core
--pom.xml
--models
----/pom.xml
--controllers
----/pom.xml
--repositories
----/pom.xml
pom.xml
Please have a look complete guide how to create multi module project in spring boot.
https://spring.io/guides/gs/multi-module/
Spring boot will component scan from the package of the class annotated with #SpringBootApplication. Component scannign means that it is looking through the classes under that package recursively, analyzing annotations, and wiring up anything it recognizes. This can include controllers, simple variables with #Value annotations, members with #Autowired, and a host of other things.
You can actually jump into the source for the #SpringBootApplication annotation and see that it expands to numerous other annotations, #ComponentScan being one of them.
If all of your modules are in a sub-hierarchy package wise from there, then they will be scanned properly anyway. Often though, sub-modules will be in a slightly different package hierarchy. In this case, you can explicitly specify #ComponentScan() in your code and inside the () you can list the base packages to component scan from.
Whether or not its a sub-module doesn't matter much at this point; its just like scanning classes in any other library you're including.
General Advice
Also, just FYI - Multi module projects can get a little hard to manage (speaking from numerous separate experiences). They can be very good if used properly though. If you're a beginner to Maven though, it may be wiser to keep separte, well-defined projects with a proper release cycle and just import them as normal dependencies.
So, I'm not for or against them, but just make sure you understand them well going in :).
I have a GitHub project where I configured a multimodule maven project:
https://github.com/cristianprofile/spring-boot-mvc-complete-example
This is Example project maven module structure:
Spring mvc rest maven module ---> service maven module ---> repository maven module
The main module should be configured like this (Spring mvc rest layer):
#SpringBootConfiguration
#EnableAutoConfiguration
//spring mvc module auto scan only its package
#ComponentScan(basePackageClasses = HelloWorldController.class)
//It needs Service bean so it will import ConfigurationService.class from
// Service maven module
#Import({ConfigurationService.class})
Complete class:
https://github.com/cristianprofile/spring-boot-mvc-complete-example/blob/develop/spring-boot-mvc-rest/src/main/java/com/mylab/cromero/controller/Application.java
It will only scan its package :
HelloWorldController.class --> com.mylab.cromero.controller;
This Rest layer use a service maven module so it is necessary to add dependency:
<dependency>
<groupId>com.mylab.cromero.core</groupId>
<artifactId>mylab-core-service-impl</artifactId>
<version>0.0.2-SNAPSHOT</version>
</dependency>
Complete pom file:
https://github.com/cristianprofile/spring-boot-mvc-complete-example/blob/develop/spring-boot-mvc-rest/pom.xml#L16
ConfigurationService.class from service maven module autoscan its packages and it will import ConfigurationRepository.class (Repository maven module)
#Configuration
//It needs repository's bean so it will import ConfigurationRepository.class from
// Repository maven module
#Import(ConfigurationRepository.class)
//service layer module auto scan only its package
#ComponentScan(basePackageClasses = ConfigurationService.class)
public class ConfigurationService {
}
Complete Service maven module code:
https://github.com/cristianprofile/spring-boot-mvc-complete-example/blob/develop/mylab-core/mylab-core-service-impl/src/main/java/com/mylab/cromero/service/ConfigurationService.java#L12
Service maven module layer has a dependency with maven repository module:
https://github.com/cristianprofile/spring-boot-mvc-complete-example/blob/develop/mylab-core/mylab-core-service-impl/pom.xml#L38
Repository module will auto configure jpa and domain classed:
#Configuration
#EnableJpaRepositories(basePackages = "com.mylab.cromero.repository")
#EntityScan(basePackageClasses=Base.class)
#ComponentScan(basePackageClasses = BaseRepository.class)
public class ConfigurationRepository {
}

Nested modules with Spring

I am developing project with Spring Framework.
I have created about 5 modules, sometimes one depend on other, but they are all on top level, and up to this point everything works fine.
Example:
Database module has only external dependencies
Identity module depends on database module
Facebook stuff module depends on identity module
Now, I have created directory in root of project called modules, and moved all modules into it (so they all were, and still are on same relative distance to each other).
All tests passes and I can build/compile and inspect classes without any problem.
However, now when I try to run only identity module (that does not require facebook stuff) spring throws me an exception, that it cannot find facebook beans. Of course it cannot, because there is no dependency, but I do not want to add this dependency. #Configuration is #Lazy so there is no point creating such #Bean anyway.
Code:
new AnnotationConfigApplicationContext(Application.class);
Application class is #Lazy #Configuration and does #ComponentScan from whole application, and as I understand it finds also #Configuration's from other modules and then - I do not know why - tried to create those #Bean's from other modules but fails as expected.
I have verified with git, that the only between working and not working states are moving those modules into new folder.
So to clarify, working/default structure is:
/.gradle
/.idea
/DatabaseModule
/IdentityModule
/FacebookModule
/.out
/.gitignore
and not working one is:
/.gradle
/.idea
/modules/DatabaseModule
/modules/IdentityModule
/modules/FacebookModule
/.out
/.gitignore
Code stays the same.
I think, that if I will add all dependencies to all modules then it will work but for obvious reasons I do not want to do this.
Am I doing something wrong?
Is there any convention, that I am breaking?
Bonus question: how are nested modules different, from ordinary folder containing modules?
EDIT:
I should also note, that all tests pass in both scenarios, however I am not using spring in tests (no dependency injection) - just new or Mock() everything

Categories