Should I annotate configuration class as #Configuration for testing? - java

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()

Related

How to load a single bean for testing in Spring boot without loading the whole context?

I have a class A that is dependent on bean B ( pretty simple bean, just a clock to be called ).
I want to unit test class A, how can this bean be loaded? #SpringBootTest loads the whole Context.
You should use the combination of two annotions:
#ExtendWith(SpringExtension.class)
#Import(
value = {
SomeSpringBean.class
}
)
whereinto #Import's value you can put your not mocking bean without spring-context building.
You can put through #Import any spring bean(class with #Configuration or #Component etc)
For mocking another bean in this test use #MockBean annotation.
The usage looks like that:
#ExtendWith(SpringExtension.class)
#Import(
value = {
SomeSpringBean.class
}
)
class SomeSpringTest {
#MockBean
private MockedBean mock;
#Autowired
private SomeSpringBean bean;
...
}
see more in javadoc:
Indicates one or more component classes to import — typically #Configuration classes.
Provides functionality equivalent to the element in Spring XML. Allows for importing #Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes (as of 4.2; analogous to AnnotationConfigApplicationContext.register).
#Bean definitions declared in imported #Configuration classes should be accessed by using #Autowired injection. Either the bean itself can be autowired, or the configuration class instance declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly navigation between #Configuration class methods.

SpringExtension without an explicit configuration?

I have a JUnit5 test with SpringExtension. All I need is environment variables to be injected via Spring's #Value:
#ExtendWith(SpringExtension.class)
class MyTest {
#Value("${myValue}") String myValue;
...
Doing so, I get an error saying:
Failed to load ApplicationContext Caused by:
java.lang.IllegalStateException: Neither GenericGroovyXmlContextLoader nor AnnotationConfigContextLoader was able to load an ApplicationContext
Of course, Spring needs to have a context configuration, so I put it into the test code:
#ExtendWith(SpringExtension.class)
#ContextConfiguration
class MyTest {
#Value("${myValue}") String myValue;
#Configuration
static class TestConfig { /*empty*/ }
...
While this works, it looks like a lot of unnecessary boilerplate code to me. Is there a simpler way?
UPDATE
One shorter variant would be to use #SpringJUnitConfig which brings both #ContextConfiguration and #ExtendWith(SpringExtension.class) out of the box.
But a configuration class (even an empty one) is still needed.
As has been pointed out in other answers and comments, you need to specify an empty configuration source, specifically a #Configuration class, XML config file, Groovy config file, or ApplicationContextInitializer.
The easiest way to do that is to create your own composed annotation that predefines the empty configuration.
If you introduce the following #EmptySpringJUnitConfig annotation in your project, you can use it (instead of #SpringJUnitConfig) wherever you want an empty Spring ApplicationContext.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Documented
#Inherited
#SpringJUnitConfig(EmptySpringJUnitConfig.Config.class)
public #interface EmptySpringJUnitConfig {
#Configuration
class Config {
}
}
You cannot run a Spring based test without a configuration. The Spring Test Context Framework (TCF) expects/requires an ApplicationContext. To create an ApplicationContext a form configuration (xml, Java) needs to be present.
You have 2 options to make it work
Use an empty configuration, emtpy XML file or empty #Configuration class
Write a custom ContextLoader which creates an empty application context.
Option 1 is probably the easiest to achieve. You could create a global empty configuration and refer that from the #ContextConfiguration.
In SpringBoot to run a spring application context, you need to use the #SpringBootTest annotation on the test class:
#ExtendWith(SpringExtension.class)
#SpringBootTest
class MyTest {
#Value("${myValue}") String myValue;
...
UPDATED:
Or if you use just a Spring Framework (without spring boot) then test configuration depends on a version of the spring framework which you use, and on the spring configuration of your project.
You can set configuration files by the using of #ContextConfiguration, if you use java config then it will be something like that:
#ContextConfiguration(classes = AppConfig.class)
#ExtendWith(SpringExtension.class)
class MyTest {
...
or if you use xml configuration:
#ContextConfiguration("/test-config.xml")
#ExtendWith(SpringExtension.class)
class MyTest {
...
both of these depends on your project configuration structure and list of beans which you need in tests.
More details about context configuration: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#spring-testing-annotation-contextconfiguration
If you use the Spring Framework older then 5.0 then you can find useful this library: https://github.com/sbrannen/spring-test-junit5

Why is WebMvcConfigurer being loaded?

I'm new to Spring and testing a Spring Data project with Postgresql/JPA and MongoDB components. My Test class has the following annotations:
#SpringBootApplication
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {SpringMongoConfig.class, PgRepository.class, MongodbRepository.class})
public class PerfTest {
#Autowired
private PgRepository pgRepo;
#Autowired
private MongodbRepository mongoRep;
For some reason the spring-context module is trying to load WebMvcConfigurer while resolving bean classes, giving me a NoClassDefFoundError.
Is the Spring Boot Autoconfigure trying to initiate a full web controller suite? If so, why? I'm really just interested in Spring Data. Should I avoid Spring Boot entirely?
You shouldn't annotate a test class as a #SpringBootApplication. This annotation is used to define a class in your main code base which contains a main() method to spin up your Spring boot container with auto-configuration.
If it's Spring data you are interested in testing then your tests will need to point to a #Configuration class which is annotated with #EnableJpaRepositories this will allow your repository interfaces to be autowired.
I don't know what is in your SpringMongoConfig class but if this contains that annotation then all you need to do is:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoConfig.class})
public class PerfTest {
If not then you need to set up this configuration and include it in the #ContextConfiguration of the test.
Another option which is a bit more heavyweight but will guarantee everything is available for your test (provided your Spring boot application is set up correctly) is to mark your test with #SpringBootTest this will automatically load the whole Spring boot context for the application. If it has trouble finding your main class you can point it in the right direction by providing the class in the annotation:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyMainClass.class)
public class PerfTest {
You need to add #Component to your repository interface. Spring loads the component class first and then #AutoWiring & Initialization of variables will happen.

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.

What is a specific use case where you will use #Configuration with #ComponentScan in Spring?

I'm trying to understand the transition from using xml annotation to java based annotation in Spring. I have these definitions
<context:annotation-config>: Scanning and activating annotations for already registered beans in spring config xml.
<context:component-scan>: Bean registration + <context:annotation-config>
is #Configuration and is #ComponentScan.
If lets say I declare all my beans with #Component (disregard first the more specific ones like #Repository, #Service etc) annotation and make sure that the packages are getting scanned by the #ComponentScan annotation, what is a particular use case where I will still annotate my class with both #Configuration together with #ComponentScan?
I ask this question because sometimes I see classes annotated with both #Configuration and #ComponentScan at the same time.
First read the following carefully:
Difference between <context:annotation-config> vs <context:component-scan>
Thus <context:component-scan> does the scan job and the same job than <context:annotation-config> does, it means work around with the DI annotations
Then now consider:
<context:component-scan> equivalent to #ComponentScan
<context:annotation-config> no equivalent for annotation.
what is a particular use case where I will still annotate my class
with both #Configuration together with #ComponentScan?
#Configuration is used to define beans about Infastructure such as Database, JMS etc...
Yes, a class can use both, It could be used for example for MVC Infrastructure #Configuration such as:
#EnableWebMvc
#Configuration
#ComponentScan("com.manuel.jordan.controller", "com.manuel.jordan.rest")
public class WebMvcConfig extends WebMvcConfigurerAdapter {
Thus from that class your are configuring MVC and indicating only to scan the MVC classes created by you for the "web side", such as: #Controller, #RestController.
This really depends on your likings and coding style. Documentation states:
Either basePackageClasses() or basePackages() (or its alias value()) may be specified to define specific packages to scan. If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.
So every time you see just #ComponentScan annotation this means that all sub-packages should be scanned. This is a reasonable approach to take with package per feature layout: when you have a #Configuration class for your feature and all #Components related to the feature in sub-packages.

Categories

Resources