How to test Java Spring Boot application without #SpringBootApplication using JUnit? - java

I created a Spring Boot Java library which has a set of utilities that perform certain functions. This library can then be used across multiple applications that require its utilities. This library was designed as a Spring Boot application to allow these utilities to be injected in the application context.
Now I wish to execute a JUnit test on one of the utilities to ensure it is working correctly. However, since this application is basically a collection of utilities and not a stand-alone application, it does not have a main class or the main method annotated with #SpringBootApplication. Now, when I run the JUnit test, it comes up with an error.
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...)
Is it possible to test this Java library, or should we write the test cases only in the application that will be using this library?

I think there is some contradiction in what you say:
Created a Library...that was designed as a Spring Boot Application.
Library can be used across multiple applications that require its utilities.
If you implement "1" then there is a module with spring boot maven/gradle plugin configured to create a JAR of the application which is a library.
But if you have, say, module X that wishes to use your library, its impossible to add the dependency on your library in this module, spring boot JAR artifacts are not JARs in a Java Sense... So this won't work in both IDE and maven (I mean technically you'll have compilation errors).
Then you write something that completely makes sense: You say that the library by itself doesn't have a main class/#SpringBootApplication annotated class. From this I conclude that its not a spring boot application, but rather a spring boot starter module.
In this case you should not use #SpringBootTest annotation since it mimics the way of starting up the spring boot application (finds main class, scans the packages according to the package structure, loads the configurations and so forth). You don't need all this. Well, maybe technically you can still create a main class annotated with #SpringBootApplication and put it into src/test/java/.../ in a relevant package, but it doesn't really makes sense.
So basically you have two choices:
You can test the utilities without spring at all as if the utility is just a Java class, mock the dependency with Mockito and you're good to go. Since these tests are fast, it you be the best option.
You can run the integartion test by means of loading the spring context with all the required beans created by the application.
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {MyLibraryConfiguration.class})
public class SampleLibraryTest {
#Autowired
private SomeBeanFromTheLibrary theBean;
#Test
public void testFoo() {
....
}
}
Now although you can use component scanning (in this case you'll need slightly different annotations), in the example I've assumed that you're using java config, register all the beans of the library there and create a spring.factories that uses this Java Configuration file to create an autoconfiguration (you add a dependency on the library in module X and it loads the beans defined in the library automatically).
This #ExtendsWith (#RunWith for junit 4) has nothing to do with Spring Boot, it behaves as a "plain" spring, you can autowire beans, create mock beans, there is caching of configurations, etc.

Related

Add maven dependency conditional on another

I have several projects that depend on a common framework, built on top of Spring Boot. This framework autoconfigures lots of the things that applications use via BeanPostProcessor and similar Spring mechanisms.
It also adds support for optional libraries that the applications can use, using #Conditional beans and optional Maven dependencies.
The problem I'm facing is that one of those optional libraries is Resilience4J and I'd like to add a dependency on resilience4j-micrometer if Resilience4j is added, so I can expose out-of-the-box all Resilience4J metrics via Micrometer.
Is there any mechanism in Maven that allows to add a conditional dependency given some condition, so I ensure that every application using Resilience4J exposes its metrics?
PS: I tried to just create a module to wrap the dependencies, but there are different Resilience4J dependencies the applications use (some apps are web and others Webflux, some use the Spring Cloud abstraction and others don't...).
You can try to manage dependencies via maven profile but it not really the best way. Your framework should integrate all libraries and your app manage dependencies with exclusions in order that Spring Boot can autoconfigure only included dependencies.
I'm not sure I'm following your question, I assume that you have a library "common".
So you have an application A (spring boot driven), and in its pom.xml you add a dependency on "common". You would like to create Beans in the application context of A if A has a dependency on Resilience4J. These beans are supposed to add expose some micrometer related stuff.
In this case you could probably use ConditionalOnClass:
In "common"'s code you could have something like this:
#Configuration
#ConditionalOnClass(Resilience4j.class) // or whatever class that can indicate that resilience4j is in the classpath, I took this for the sake of example, I don't know whether this class really exists
public class CommonConfiguration {
#Bean
public YourMicrometerIntegrationBean yourMicrometerIntegrationBean() {
return new YourMicrometerIntegrationBean();
}
.. other beans ..
}
This solution assumes that you have a dependency (optional ?) at the level of maven in "commons" to resilience4j. If you don't want this for some reason, you could create your custom condition (I believe you know how to do that based on the details that you've supplied in the question you have a pretty advanced setup, let me know if you need more details on that), and this custom condition will evaluate to "true" if Class.forName(<FULLY_QUALIFIED_NAME_OF_RESILIENCE_4_J>) won't throw an exception so that you'll see that its available in runtime.

How to share Repository and Service classes between 2 projects

I am working on 2 projects, one web app (Spring MVC) and one standalone backend service application (Spring boot) that heavily interact together. I am using hibernate for both and they are both coded using the Netbeans IDE.
My "issue" is that i end up with duplicate code in both project, mainly in the Repository and Service layers. My entities are obviously also duplicated since both projects use the same database.
Is there a way to make some sort of class library (a third project maybe?) and put all the common code in there? If that is indeed possible, how do you then change each project so they can still access this code as if it were part of them? I was thinking of putting all my Repositories, Services and entities in there to avoid code duplication and greatly reduce the risk of error.
Thank you!
Separate those Repository and Service classes to a submodule.
The structure looks like:
-- your app
-- api (dependent on `common` module)
-- webapp (dependent on `common` module)
-- common
Then the problem is to initialize beans inside common module. AFAIK, you have two options:
In #Configuration class of api or webapp module, add base packages of common module to component scan packages
In api or webapp resources folder, add Spring configuration factory
/src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=your.path.AutoConfiguration
Define service/repository #Bean inside AutoConfiguration class
I am assuming in this answer your projects are connected to each other
You can set multiple properties within one Spring project, where you store your database connection parameters etc. with the help of multiple property files.
For example:
application-web.properties
application-backend.properties
You can use these in your project, by activating the needed properties file per application. The profile names will be web and backend in these cases.
When using maven, this is the command line I am using:
mvn spring-boot:run -Drun.profiles=<<profile>>
Now, back to your java code.
If there are classes only one of your application is using, you can specify this by 'profile'. Example:
#Controller
#Profile({ "web" })
public class WebEndpoint {
}
This way you can make the shared code available for both applications, without duplicating most of the code.

Spring - dependency injection - testing with different implementation

One of the main advantage of using spring dependency injection is for testing the functionality using same interface with different implementation without making any changes in the code, that is through injecting these different implementations(dependencies) in configuration file.
Lets take an example where we have developed our application with java configuration/annotation based (No .xml files at all).
We have done a code freeze and have deployed the code in server.
Now for a QA team to perform testing they need to inject different implementations for the interface by making changes in configuration file without touching code.
If its a .xml file, devOps team can inject the different implementation by injecting that bean name and can restart the server.
But since we have used the annotations based/java based configuration, How can we achieve this ?
Thanks in advance.
One of the main advantage of using spring dependency injection is for
testing the functionality using same interface with different
implementation
One of main advantages of Spring is indeed the dependency injection facility.
But you will also find very often cases where you have beans with a single implementation :
beans that rely on an interface but there is only one implementation for it.
bean that don't rely on any interface but are straight classes that you want to turn into injectable beans.
We have done a code freeze and have deployed the code in server. Now
for a QA team to perform testing they need to inject different
implementations for the interface by making changes in configuration
file without touching code.
Spring and more generally dependency injection pattern/frameworks are not designed to perform hot swapping or implementation modification of a deployed component without repackaging the component.
At startup, Spring creates its context and loads all required beans for the application in its container.
If you want to change configurations of some beans, the most clean and side effect less way is destroying the spring context/container, repackage the application with the needed changes and restart it.
If its a .xml file, QA team can inject the different implementation by
injecting that bean name and can restart the server.
Ideally, the QA team should test the implementation that you deploy in QA env and that will be used by final users to stay the closest of the real functioning of the application.
Now, if because of some specific constraints, some components to test by the QA should be mocked/stubbed in a some way, just create a different build for that.
Spring Boot Profile and Maven Profile features can help for.

Forcing external java (jar project) library beans to be managed by Spring fabric

I have two project the first is a spring boot app and the second one i.e. java static library project that is not dependent on anything else except java. In the past those two projects were one project however i separated them since they represent two different logical components, and that java library is used in other sub projects as well. Now since the statical library is simply a jar i can not instantiate classes of that jar based on the interface name provided such as we do it in spring for example if my interface was located in path:
edu.university.ServiceLayer.StudentInterface
i would easily do:
Student object = (Student) applicationContext.getBean("StudentInterface");
and that gives me the student object
Now i would like to do the same with the external java library. Since i have never done this, my question is what would be the best way to do it if keeping in mind that i would like to keep that library not dependent on anything else except java.
In my spring boot project in order to do that i needed simply need to annotate the selected bean with the correct artifcat i.e. #Component, #Repository #Service etc. and those beans are then automatically managed by the spring fabric. I.e. I can then seen them by printing the BeanDefinitionNames() of the applicationContext i.e.
String[] beanNames = applicationContext.getBeanDefinitionNames();
But that trick does not work with external jar. Now what would be the best compromise for this constellation, i.e. shell i really add spring dependency to my jar java library or is there any magical way i can do it without adding those dependency to my library. Again my target is to allow spring to manage selected beans of the external library i.e. i would like them to appear under:
applicationContext.getBeanDefinitionNames()
is there any pattern-like way that is used to accomplish this?
many thanks for the ideas.
This doesn't relate to Spring Integration at all.
Plus you have to read more documentations.
Any class available in the CLASSPATH can be instantiated as a bean in the Spring Container, e.g.
#Bean
public Foo foo() {
return new Foo();
}
When that Foo is in your jar. No need to modify them for those stereotype annotations.
BTW, to be more clear you even can create beans for Java native classes:
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Date now() {
return new Date();
}

Generic Spring services from external library?

I'd like to define some commonly used or generic service classes that should be used/shared by different projects. These common services should already make use of #Transactional, #Autowired and other Spring related stuff. So, I somehow have to define a spring context for these services to work.
Is it possible to put these services in a single external jar library that can then be used/imported by other (child)-projects? How could I create such a "personal framework"?
What you could do is create a maven (or gradle) module that contains the code you desire to be reusable and also have a spring configuration (either XML or Java Config) that will be imported by the project that uses the module (either with or having component scanning pick up the #Configuration class of the module).

Categories

Resources