How to prevent the SpringJUnit4ClassRunner load all #Configuration classes - java

In my unit test class, I have the following configuration:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = WebAppConfigTest.class)
public class ExampleTest {
But after loading the WebAppConfigTest class, it's loading my WebAppConfig class that has #Configuration and is out of the test package (src/test/java).
Note: the class WebAppConfig is not configured to be loaded into the unit test, but still is being charged.
WebAppConfig Class
#EnableWebMvc
#ComponentScan(basePackages = {"br.com.example"})
#PropertySource(value="classpath:application.properties")
#Configuration
public class WebAppConfig extends WebMvcConfigurerAdapter {
WebAppConfigTest Class
#ComponentScan(basePackages = {"br.com.example"})
#Configuration
public class WebAppConfigTest {
How to prevent this class out of the test package is loaded?

Spring doesn't differentiate packages from src/test/java or src/main/java for your component scan.
#ComponentScan(basePackages = {"br.com.example"})
is essentially scanning all the #Configurations,all packages within current package and sub-packages starting from "br.com.example".
These are the options available for you:
Change your package structure to give a more specific package to scan for test classes.
You can use the filters in #ComponenScan to include/exclude specific packages/classes. You can avoid the unwanted classes to be picked up by spring when loading the application context this way.
If its possible to handpick the required configurations to load the context for tests, you can even remove the #ComponentScan altogether and specify all the configuration classes in "classes" attribute.
You can use #Profile in your configuration classes and #ActiveProfiles in your test classes to map the configuration classes to be loaded in specific profiles.

Related

Spring Boot scanBasePackages unable to find beans from dependency

I have following:
#SpringBootApplication(scanBasePackages = {"com.my.package","com.my.package.mylibrary"})
#EnableAsync
#EnableSwagger2
#ServletComponentScan
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class})
public class MySpringBootApplication {....}
This application has package com.my.package, and it also has a library dependency containing spring beans I want to autowire in this application, and those beans are in package com.my.package.mylibrary inside library.
So I have put both for scanBasePackages. But Spring is not able to find beans from the library?
Edit:
From library, I have:
package com.my.package.mylibrary.repository;
....
public interface MyRepository extends JpaRepository<..., ....> {....}
In application, I have:
package com.my.package.controller;
....
#RestController
public class MyController {....}
MySpringBootApplication resides in com.my.package.
Error:
Exception in thread "main" java.lang.NoClassDefFoundError: com/my/package/mylibrary/repository/MyRepository
at com.my.package.MySpringBootApplication.main(MySpringBootApplication.java:32)
Caused by: java.lang.ClassNotFoundException: com.my.package.mylibrary.repository.MyRepository
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
I added #EnableJpaRepositories for repository package. Now I see error related to entity MyEntity which MyRepository is based upon.
"java.lang.TypeNotPresentException: Type com.my.package.mylibrary.domain.MyEntity not present
So I added #EntityScan for "com.my.package.mylibrary.domain", but that makes application stuck infinitely.
First of all, you don't need to add scanBasePackages attribute in #SpringBootApplication as the base package is com.my.package.
If package is totally different, then you could have added it.
Spring Boot will automatically pick up the bean if the base package is same.
There is something called as separation of concerns that you should follow when you are writing code.
Update your MySpringBootApplication class to this :
#SpringBootApplication
#ServletComponentScan
public class MySpringBootApplication {....}
Create a separate config for asynchronous method execution.
#Configuration
#EnableAsync
public class AsynchronousConfig {.....}
Create a separate config class for Swagger 2.
#Configuration
#EnableSwagger2
public class SwaggerConfiguration {....}
Create separate config to exclude configuration.
#Configuration
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class})
public class ExcludeConfigurationFile {....}
Note: Spring boot auto configuration will automatically pick up these #Configuration files
This should work.
You might want to scan the packages seperately and change your JpaRepository to CrudRepository. The configuration to seperate the layer is as follow.
#SpringBootApplication(scanBasePackages = {"com.my.package.controller"})
#EnableJpaRepositories(basePackages = {"com.my.package.mylibrary.repository"})
#EntityScan(basePackages = {"com.my.package.mylibrary.domain"})
public class MySpringBootApplication {
public static void main(String[] args) {
MySpringBootApplication.run(MySpringBootApplication.class, args);
}
}

What is the difference between #ComponentScans and #ComponentScan?

I see that we have #org.springframework.context.annotation.ComponentScans and #org.springframework.context.annotation.ComponentScan.
How do we use #ComponentScans() ?
How is #ComponentScans() different from #ComponentScan({"com.org.abc", "com.org.xyz"})
Spring can automatically scan a package for beans if component
scanning is enabled.
#ComponentScan configures which packages to scan for classes with
annotation configuration. We can specify the base package names
directly with one of the basePackages or value arguments (value is an
alias for basePackages)
#Configuration
#ComponentScan(basePackages = "com.baeldung.annotations")
class VehicleFactoryConfig {}
Also, we can point to classes in the base packages with the
basePackageClasses argument:
#Configuration
#ComponentScan(basePackageClasses = VehicleFactoryConfig.class)
class VehicleFactoryConfig {}
Both arguments are arrays so that we can provide multiple packages for
each.
If no argument is specified, the scanning happens from the same
package where the #ComponentScan annotated class is present.
#ComponentScan leverages the Java 8 repeating annotations feature,
which means we can mark a class with it multiple times:
#Configuration
#ComponentScan(basePackages = "com.baeldung.annotations")
#ComponentScan(basePackageClasses = VehicleFactoryConfig.class)
class VehicleFactoryConfig {}
Alternatively, we can use #ComponentScans to specify multiple
#ComponentScan configurations:
#Configuration
#ComponentScans({
#ComponentScan(basePackages = "com.baeldung.annotations"),
#ComponentScan(basePackageClasses = VehicleFactoryConfig.class)
})
class VehicleFactoryConfig {}
You can found more Spring Bean Annotations
Take a look at documentation:
ComponentScans Container annotation that aggregates several
ComponentScan annotations.
ComponentScan Configures component scanning directives for use
with #Configuration classes.

Spring Boot Test - I do not want to load all #Configuration classes for a particular test

I have a half-dozen classes in an application annotated with #Configuration and am writing unit tests.
I have one #Configuration class that sets up Quartz, and one that deals with setting up Camel.
In my Camel unit test, I only want to load the #Configuration class that sets up Camel, because it does not care about Quartz (and vice-versa).
How do I tell my Spring Boot test to only bootstrap certain configuration-annotated classes? #TestConfiguration does not do that...
#Configuration
public class QuartzConfig {
...
}
#Configuration
public class CamelConfig {
...
}
#RunWith(SpringRunner.class)
#SpringBootTest
#SOME ANNOTATION THAT SAYS ONLY LOAD CamelConfig.class???????
public class CamelOnlyTest {
....
}
By using the classes parameter of #SpringBootTest, you can specify which classes to use for configuration.
From the docs:
public abstract Class[] classes
The annotated classes to use for loading an ApplicationContext.

Spring boot test configuration not being picked

I am writing an integration test for my application, and want to use a custom webmvc configuration for my tests
I have three classes in my base package com.marco.nutri:
Application(which is annotated with #SpringBootApplication)
MvcConfig(#Configuration and #EnableWebMVC)
SecurityConfig(#Configuration and #EnableWebSecurity)
My test is in the package br.com.marco.nutri.integration.auth:
#RunWith(SpringRunner.class)
#SpringBootTest(classes={Application.class, WebMvcTestConfiguration.class, SecurityConfig.class})
public class ITSignup {
//Test code
}
I have a test config class in the package com.marco.nutri.integration:
#TestConfiguration
#EnableWebMvc
public class WebMvcTestConfiguration extends WebMvcConfigurerAdapter {
//Some configuration
}
But when I run my test, the MvcConfig.class is picked instead of WebMvcTestConfiguration.class
What am I doing wrong?
you can annotate your test configuration with #Profile("test") and your real one with #Profile("production")
and in your properties file put the property spring.profiles.active=production and in your test class put #Profile("test"). So when your application starts it will use "production" class and when test stars it will use "test" class.
from documentation
Unlike regular #Configuration classes the use of #TestConfiguration
does not prevent auto-detection of #SpringBootConfiguration.
Unlike a nested #Configuration class which would be used instead of a
your application’s primary configuration, a nested #TestConfiguration
class will be used in addition to your application’s primary
configuration.

Should I annotate configuration class as #Configuration for testing?

I spent some time resolving problem with missing org.joda.time.DateTime->java.util.Date converter in Spring Data (which should be enabled by default when Joda-Time is on a classpath). I have found a reason, but it generated a question about #Configuration annotation in Spring.
Standard application config using AbstractMongoConfiguration from spring-data-mongodb:
#Configuration
#ComponentScan
#EnableMongoRepositories
public class AppConfig extends AbstractMongoConfiguration { ... }
A test which explicit uses AppConfig class (with Spock, but internally mechanisms provided by spring-test are used):
#ContextConfiguration(classes = AppConfig)
class JodaDocRepositorySpec extends Specification {
#Autowired
private JodaDocRepository jodaDocRepository
def "save document with DateTime"() {
given:
def jodaDoc = new JodaDoc(DateTime.now())
when:
def savedJodaDoc = jodaDocRepository.save(jodaDoc)
then:
savedJodaDoc.id
}
}
It works fine. But when #Configuration annotation in AppConfig is removed/commented:
//#Configuration
#ComponentScan
#EnableMongoRepositories
public class AppConfig extends AbstractMongoConfiguration { ... }
the test fails with:
org.springframework.core.convert.ConverterNotFoundException:
No converter found capable of converting from type org.joda.time.DateTime to type java.util.Date
AFAIK it is not needed to use #Configuration for the configuration class when it is explicit registered in the context (by classes in #ContextConfiguration or a register() method in AnnotationConfigWebApplicationContext). The classes are processed anyway and all declared beans are found. It is sometimes useful to not use #Configuration to prevent detecting by a component scan when there are 2 similar configuration classes in the same packages in a test context used by different tests.
Therefor I think it could a bug in Spring which causes to different internal beans processing in the context depending on an usage or not a #Configuration annotation. I compared Spring logs from these two cases and there are some differences, but I'm not able to determine what are they caused by in the Spring internal classes. Before a bug submission I would like to ask:
My question. Is there an explicable reason why Spring for the same configuration class (pointed explicit in #ContextConfiguration) uses (or not) converters for Joda-Time depending on an existence of a #Configuration annotation?
I created also a quickstart project reproducing the issue. spring-data-mongodb 1.3.3, spring 4.0.0, joda-time 2.3.
It's everything OK in this behaviour. AbstractMongoConfiguration is annotated by #Configuration, but in fact this annotation is not #Inherited, so you have to explicitly annotate your class.
When you remove #Configuration annotation then your AppConfig class is not a full configuration. It's processes as a lite configuration just because it contains methods annotated by #Bean - please refer to methods in org.springframework.context.annotation.ConfigurationClassUtils
isFullConfigurationCandidate()
isLiteConfigurationCandidate()
isFullConfigurationClass()
Finally only full (annotated by #Configuration) configuration classes are processes and enhanced by configuration post processors - look at ConfigurationClassPostProcessor.enhanceConfigurationClasses()

Categories

Resources