Test spring classes in separate gradle subproject - spring

My project is separated into several gradle subprojects (modules). I have a module which contains several spring components/beans. I want to test these beans using junit, mockito and springboottest with features like autowired and mockbean. I am using
#RunWith(SpringJUnit4ClassRunner::class)
#SpringBootTest
annotations, but when I try to run a test I get
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
This happens because there is no main class (#SpringBootApplication) in this module.
One can avoid this by creating a mock main class like
#SpringBootApplication
class TestApp {
}
Is there a way to make it work without creating a mock main class?

If you want to run test in sub-module you need to define some configuration class. It can be #Configuration with #ComponentScan located in src/test/java root package of sub-module, so that it wouldn't pollute your production code.
With such test configuration, just use #SpringBootTest(classes=YourTestConfiguration.class).
Maybe you want to look at new annotation since Spring Boot 1.4.x called #TestConfiguration. That one is specifically tailored towards test only configs.

Related

Mock Bean for all JUnit Tests in Spring Boot

In Spring Boot, is there a way to mock a single bean for all existing JUnit tests, without changing the existing test classes (e.g., by adding an annotation or adding inheritance)? Like injecting a bean globally via configuration.
Assuming you are using #SpringBootApplication in your main sources to define the Spring Boot application, you'll already have component scanning enabled for everything in that package (including nested packages).
When running tests, the classes (typically) in src/test/java are also added to the classpath, and are therefore available to be scanned as well.
For example, if you defined your #SpringBootApplication at com.example.boot.MySpringBootApplication, then com.example.boot.MyTestConfiguration would be eligible for component scanning, even though the former is in src/main and the latter in src/test. Putting it in the src/test/java directory would ensure that it only has an effect while running tests.
You can then define any "global" beans you would like in that configuration.
Using the package/class names I provided:
// File: src/test/java/com/example/boot/MyTestConfiguration.java
#Configuration // this will get component-scanned
public class MyTestConfiguration {
#MockBean
MyBean myGlobalMockBean;
}
Then, so long as you don't omit that Configuration from the Context Configuration, the MockBean should always be present under test.

Unit test in multi-module spring boot project

I have multi-module project with similar structure as below:
server (which includes Application Context Configuration) and other configurations
shared (Utility classes used by other modules)
service (module with various repository and services)
transaction (module which handles transaction)
I need to write test for the project but I cannot change the project structure. I created a test in my transaction module.
First I got
Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
I solved it by Creating a #Configuration file in the test folder like so
#Configuration
#ComponentScan("com.mohen")
public class TestConfig {
}
And then I used it in the #SpringBootTest(TestConfig.class) .I was able to autowire, the IDE did not show any sign of error. But when I run my tests I get NoSuchBeanDefinitionException from a different class that is trying to autowire a dependency from the service module.
How to solve these issues?
The main configuration file of the application looks like
#SpringBootApplication(scanBasePackages = "com.mohen")
#EnableScheduling
#EnableAsync
#Import(value = {SSIpFilter.class, MainConfig.class})
public class Application extends SpringBootServletInitializer {...}
The MainConfig.class contains componentScan and Import annotation.
If I try to Import the MainConfig.class in my test I get a suggestion to add a dependency to the server module, which I would not want to do.
Also the entire application uses a single property file (yml). Where should I keep my property file for the test?
EDIT
I managed to run the tests, a dataJpaTest and an integration test, but it loads the entire application context.
Now the problem is, the tests that pass normally , fail when I build my project ./gradlew clean build
I get
java.lang.NoClassDefFoundError
in some classes and
Caused by: javassist.NotFoundException
in other.
I have tried adding the javaassist library but it doesn't work.
Any idea?
I found the solution to my question. Due to the project being multi module, the classes and the packages were not being recognized by other modules.
I made a few changes in my build.gradle files of the modules.
testRuntime project(':shared')
I added the above in the dependencies and also added
jar {
enabled = true
}
bootRepackage{
enabled = false
}
The jar creates a simple non executable jar file while the bootRepackage disables the creation of an executable jar which by default is its nature.

Springboot build not working because of test configuration

I have started a spring boot project using start.spring.io.
But I am getting this error-
I have read various articles on the internet about this issue and they all say about putting my tests in the same package as my Main class.
But I already have the same.
Could you point out what is wrong with my configuration?
The exception is pretty clear: You are missing a configuration for your spring context. What you need to do is to add the configuration classes for your context like so:
#SpringBootTest(classes = { TestConfiguration.class })
whereas your TestConfiguration class must be annotated with
#Configuration
and/or
#EnableAutoConfiguration
There you can add configurations to your liking. You can of course also use your DatabaseApplication class as Configuration although Im wouldn't recommend that.
The search algorithm works up from the package that contains the test until it finds a #SpringBootApplication or #SpringBootConfiguration annotated class. As long as you’ve structure your code in a sensible way your main configuration is usually found.
Make Sure your DatabaseApplication class is annotated with #SpringBootApplication .

Spring AOP aspect doesn't get applied if included from an external jar with different package name

I have a spring boot rest service that included an external project in pom as it's dependency. That external project is basically a jar that has spring AOP code.
The base package in my main application that includes this external jar with spring AOP code is x.y.z
The class in external jar where the #before advice is, is under the package a.b.c
With this class under a.b.c package, it doesn't get recognized by the main application where I want to use the spring aop implementation and apply the aspect. However, when I change it's package from a.b.c to x.y.z (which I really can't do in real life) it works fine.
I know that in spring boot service which happens to be the including service, it scans everything under root package given in the application class, x.y.z in this case and that is why aspect works fine if it's class is under x.y.z.
however, the problem is that this spring app jar will be used across multiple applications. So changing package name like this is not an option.
Is there a way to accomplish this without changing the package name of the class where spring app code is ?
Probably component scan is only activated for your application class packages by default. You can extend it to multiple packages, including the aspect package:
XML style configuration:
<context:component-scan base-package="x.y.z, a.b.c" />
Annotation style configuration:
#ComponentScan(basePackages = {"x.y.z", "a.b.c"})
Disclaimer: I am not a Spring user, only an AspectJ expert. I just knew that you can configure component scan, googled the syntax for you and hope it is correct.
Please define the bean (of jar project )inside main application. Give the #ComponentScan(basePackages = {"x.y.z", "a.b.c"}) as well as #EnableAspectJAutoProxy. Also include below piece of code.
ex:
` #Bean
public LoggingHandler loggingHandler()
{
return new LoggingHandler();
}`
Also annotate external jar code with:
`#Aspect
#Component
public class LoggingHandler {`
What #kriegaex suggests is correct. In addition to that, please make sure you are using #Component along with #Aspect. Since #Aspect is not a Spring annotation, Spring won't recognize it and hence your aspect won't be registered. So, using #Component is mandatory to getting aspects to work in Spring environment.

Import by name in Spring Java configs

Say I have 3 Spring/Maven projects:
api-spec: Contains interface MyService.
api-impl: Contains class MyServiceImpl which implements MyService. Also contains class MyServiceConfiguration which is a Spring #Configuration, that defines a bean of type MyServiceImpl.
main: Contains a Spring application setup with Spring JavaConfig (e.g. a #SpringBootApplication). It has a bean with an #Autowired MyService myService field, which works as its configuration class is annotated with #Import(MyServiceConfiguration.class).
I would like the main-project to have api-spec as a Maven compile dependency and to have api-impl as a runtime dependency (to prevent us from making "hard" dependencies from the main project to the api-impl project by mistake). This is not possible, because #Import takes an array of Classes - e.g.: #Import(MyServiceConfiguration.class). I would like something like #Import("my.package.MyServiceConfiguration") instead.
Using class path scanning is not an option (we have seen too many beans getting picked up by accident), and I would prefer not having to use XML files. We could use SpringApplicationBuilder.source(..) as it accepts a class name as a String - but I can't find a way to use that in my tests...
Compile time check is one of the advantages of java config, so I don't think that it's possible to do such thinks with Java. As for me you should use XML to handle this. It doesn't mean that you should do all your configuration in XML, most of the beans of your api-impl module can be in Java and just imported to XML where will be only beans that you are going to change in runtime.
If you don't want to use XML maybe you should consider to use Groovy config instead:
https://spring.io/blog/2014/03/03/groovy-bean-configuration-in-spring-framework-4

Resources