Spring Testing: How to enable auto-scan of beans - spring

For example, now in each test class I have to do
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
I want to get rid of
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
and want Spring to scan all the beans in my project.
How can I do that?

You can do this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class MyTest {
#Test
public void testSomething() {
}
#Configuration
#ComponentScan("basepackage")
public static class SpringConfig {
}
}
By default #ContextConfiguration will look for static inner classes annotated with #Configuration, which is why this set up will just work.
You can get rid of loader param altogether, that is not required

If you have your spring configuration in an xml file you would use something like:
#ContextConfiguration(locations="classpath:applicationContext.xml")
If you use Java Config then you would use
#ContextConfiguration(classes=Config.class)
I used generic names in the above samples, you'll of course need to adapt to your project's configuration.
In both cases Spring's component scanning will need to be enabled for Spring to pickup the annotated classes.

You can also simply add #SpringBootTest if using Spring Boot.

#TestConfiguration
#ComponentScan("basepackage")
public class TestConfig{
}
Adding a config class lets spring to load application context.
This solved this issue for me.

Related

How to disable #Configuration initialization in WebFluxTest?

I would like to write tests for reactive controller using #WebFluxTest annotation, mocking all dependencies.
#WebFluxTest(controllers = MyController.class)
public class MyControllerTest {
#MockBean
SomeService service;
#Autowired
WebTestClient webClient;
//some tests
}
From what I understand, the WebFluxTest annotation shall apply only configuration relevant to WebFlux tests (i.e. #Controller, #ControllerAdvice, etc.), but not another beans.
My spring boot app contains a number of #Configuration classes that configure a number of beans (annotated as #Bean). Some of those configurations have also dependencies (autowired by constructor).
#Configuration
#RequiredArgsConstructor
public class MyConfig {
private final AnotherConfig anotherConfig;
#Bean
//...
}
When I run my web flux tests, I can see the context initialization contains an attempt to initialize the MyConfig (and it fails because of the missing dependency which comes from 3rd party auto-configured lib). How can I configure the test to skip initialization of all of these?
I am able to exclude the problematic configuration class only by excluding auto configuration of the whole app.
#WebFluxTest(controllers = MyController.class, excludeAutoConfiguration = {MyApplication.class})
public class MyControllerTest { ... }
where MyApplication is the spring boot app autoscanning those configuration classes.
But how can I achieve to skip initialization of MyConfig only? Or even better, how can I achieve to only include a list of configurations to be initialized?
Add
#ActiveProfiles("YOUR_ENV_OTHER_THAN_TEST")
below or above #Configuration
For multiple environments..
#ActiveProfiles(profiles ={env1, env2,env3})

How Do I Manually Wire A Spring Boot Integration Test?

Normally, I would test the web layer in a Spring project like this:
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SpringBootDemoApplicationTests extends AbstractTestNGSpringContextTests {
#LocalServerPort
int randomServerPort;
#Autowired
private TestRestTemplate restTemplate;
However, I currently have a difficult back end that requires a specific #TestConfiguration class to manually instantiate the test dependencies using beans.
This ultimately means that I can't use the #SpringBootTest annotation as it will try to create conflicting beans and fail to instantiate others.
If I am not using the #SpringBootTest annotation, I can manually create the TestRestTemplate instead of autowiring it, but what do I need to do to start the embedded local server on a random port?
I would still continue using #SpringBootTest, and combine that with using #Profile on your configuration classes.
That way you could have a configuration which is only used during tests, by using #ActiveProfiles on your #SpringBootTest classes. In the same way you can turn other config classes on or off depending on whether you want them to load or not.
For example on your test would have the following
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
#ActiveProfiles("unittest")
public class SpringBootDemoApplicationTests extends AbstractTestNGSpringContextTests {
...
}
Then create a configuration class which will instantiate your components the way you want them in test
#Profile("unittest")
#Configuration
public void TestConfiguration {
...
}
And you can use profiles to stop your other configuration class from loading during tests.
#Profile("!unittest")
#Configuration
public void ProdConfiguration {
...
}

application.properties not read with #EnableAutoConfiguration and custom spring boot starter

I try to create a simple custom spring boot starter that read property in application.properties :
#EnableConfigurationProperties({ CustomStarterProperties.class })
#Configuration
public class CustomStarterAutoConfiguration {
#Autowired
private CustomStarterProperties properties;
#Bean
public String customStarterMessage() {
return properties.getMessage();
}
}
with its ConfigurationProperties :
#ConfigurationProperties(prefix = "custom.starter")
public class CustomStarterProperties {
private String message;
/* getter and setter */
...
}
There is also the corresponding application.properties and META-INF/spring.factories to enable the autoconfiguration.
I have another project that declares this starter as a dependency and in which I write a test to see if the customStarterMessage Bean is created :
#RunWith(SpringRunner.class)
#EnableAutoConfiguration
public class TotoTest {
#Autowired
String customStarterMessage;
#Test
public void loadContext() {
assertThat(customStarterMessage).isNotNull();
}
}
This test fails (even with the appropriate application.properties file in the project) because the application.properties seems to not be read.
It works well with a #SpringBootTest annotation instead of the #EnableAutoConfiguration but I would like to understand why EnableAutoConfiguration is not using my application.properties file whereas from my understanding all the Spring AutoConfiguration are based on properties.
Thanks
#EnableAutoConfiguration on test classes don't prepare required test context for you.
Whereas #SpringBootTest does default test context setup for you based on default specification like scanning from root package, loading from default resources. To load from custom packages which are not part of root package hierarchy, loading from custom resource directories you have define that even in test context configuration. All your configurations will be automatically done in your actual starter project based on #EnableAutoConfiguration you defined.

Can #ContextConfiguration in a custom annotation be merged?

I am working on custom Spring Boot starters. In a test starter what I wanted do to is to implement a composed annotation, which would add additional #Configuration classes to the ApplicationContext (and possibly use this annotation in a TestExecutionListener). ex:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#ContextConfiguration(classes = AdditionalTestConfiguration.class)
public #interface ComposedAnnotation {
}
And use that in my Spring Boot integration test:
#RunWith(SpringJUnit4ClassRunner.class)
#WebIntegrationTest
#SpringApplicationConfiguration(Application.class)
#ComposedAnnotation
public class SomeTest {
}
No inheritance is involved. Unfortunately, it does not seem to work. I doubt it's a Spring Boot thing, rather Spring testing framework itself.
Is there any way I can achieve expected result?
You're right: this is not an issue with Spring Boot. But it's also not an issue with spring-test.
Rather, it's the intended behavior of Spring in general. For details, check out my answer to this question: #ActiveProfiles in meta annotation and on test class not working
In summary, you cannot achieve this with two #ContextConfiguration annotations declared on an individual test class (either directly or as meta-annotations).
However, I just came up with a trick that will allow you to achieve this. Specifically, you can create an ApplicationContextInitializer (ACI) that registers one or more #Configuration classes. In your composed annotation, you can then register this ACI to register the always present #Configuration classes. And when the composed annotation is actually used, it can declare additional #Configuration classes like normal.
I just submitted a working example in this commit.
Basically, the code would look something like this:
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, initializers = FooConfigInitializer.class)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface ComposedContextConfiguration {
#AliasFor(annotation = ContextConfiguration.class, attribute = "classes")
Class<?>[] value() default {};
}
public class FooConfigInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
#Override
public void initialize(GenericApplicationContext applicationContext) {
new AnnotatedBeanDefinitionReader(applicationContext).register(FooConfig.class);
}
}
And you can use it like this:
#RunWith(SpringRunner.class)
#ComposedContextConfiguration(BarConfig.class)
public class InitializerConfiguredViaMetaAnnotationTests { /* ... */ }
Your ApplicationContext will then be loaded from FooConfig and BarConfig.
The above examples obviously do not use Spring Boot, but the same principles should also be applicable to #SpringApplicationConfiguration.
Regards,
Sam (author of the Spring TestContext Framework)

What is the difference between grouping config via #import and #ContextConfiguration.?

What is the difference between below two ways of loading configuration.
Two independent config classes are loaded via #ContextConfiguration in test class.
Import one config into another config and load the single into #ContextConfiguration in test class.
I thought both pushes the configuration into the common pool. But i see a difference. I have two global interceptor one is in java.config and another in xml.config. If i follow 2nd approach above, both interceptors are loading.
But if i follow 1st approach, only one interceptor is loading based on whether i'm invoking xml based gateway or java based gateway.
https://github.com/manojp1988/Learning/tree/JavaDSL/Sample1
#RunWith(SpringJUnit4ClassRunner.class)
#ContextHierarchy({
#ContextConfiguration(locations = {"/applicationContext.xml"}),
#ContextConfiguration(classes = SpringConfiguration.class),
})
public class SampleTest {}
Updated:
#Bean
#GlobalChannelInterceptor(patterns = "*_EL*", order=3)
public WireTap wireTap() {
If you follow approach #1, both interceptors are actually being loaded... just in different ApplicationContexts.
#ContextHierarchy instructs the Spring TestContext Framework to load a hierarchy of contexts.
Thus, the two configuration setups you have are not identical with regard to contexts. When you use #ContextHierarchy, the XML components can only see other beans defined in the XML ApplicationContext (i.e., the top level of the hierarchy); whereas, the Java DSL components can see all components (i.e., those configured in Java Config and those configured in XML, since the XML context is the parent of the Java Config context).
I believe this is actually what you want...
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringConfiguration.class)
public class SampleTest { /* ... */ }
#Configuration
#ComponentScan(basePackages = "com.model")
#EnableIntegration
#IntegrationComponentScan
#ImportResource("/applicationContext.xml")
public class SpringConfiguration { /* ... */ }
If you don't want SpringConfiguration to import the XML config, you can alternatively use the following to achieve the same behavior:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class SampleTest {
#Configuration
#Import(SpringConfiguration.class)
#ImportResource("/applicationContext.xml")
static class Config {}
/* ... */
}
Regards,
Sam (author of the Spring TestContext Framework)

Resources