I am working on a Spring project where the unit tests have their own config called UnitTestConfig which has several beans defined similar to the main application file (almost a replica). Keeping the structure intact, i am making some changes in main application server code, however this throws error in UnitTestConfig, because it doesnt have the required beans for injection. These beans are not used in Unit tests, is there a way I can prevent UnitTestConfig from trying to inject those? Its a big casacade effect, since A injects, B injects C, and so on, and it is expecting all those Beans. Any way I can tell Spring config that i dont want to inject those beans or have them as null ?
@Configuration
public class UnitTestConfig {
@Inject
private Environment env;
@Bean
public A a() {
return new A();
}
In order to not inject A's fields when required, i added a @Lazy over the field and it seemed to work, but I would prefer any modifications for this to be on the test config side, and not modify main application code just to fix test issues. Any suggestions?
CodePudding user response:
This is called cyclic bean dependency. There are many ways to resolve this issue. Annotate with @Lazy in constructor parameter. Instead of constructor injector use setter injection or In application.properties file write spring.main.allow-bean-definition-overriding=true
CodePudding user response:
This is a very common problem with most of the applications and as application grow, it becomes difficult to add unit tests having common configuration. And running a unit test becomes a nightmare as you have to take care of unnecessary context loading.
We eventually started having test profiles, to work-around the problem of having unnecessary beans being loaded. And loading beans only for certain profiles. Something like this-
How can I disable creating bean with @Component annotation in Spring?
But this can create its own issues - creation of multiple profiles and management of those profiles. This can help you though if managing profiles is not an issue.
Alternatively, we stopped using a common context for unit testing and started testing classes in isolation with a static test context class and mocking all non-required beans. Something like this -
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class SomeJavaClassTest {
@Autowired
private Bean1 bean1;
@Autowired
private Bean2 bean2;
@Autowired
private Bean3 bean3;
@test
public void method1Test() throws Exception {
}
@test
public void method2Test() throws Exception {
}
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return Mockito.mock(Bean1.class);
}
@Bean
public Bean2 bean2() {
return new SomeJavaClass();
}
@Bean
public Bean3 bean3() {
return Mockito.mock(Bean3.class);
}
@Bean
public PropertySourcesPlaceholderConfigurer properties() throws Exception {
PropertySourcesPlaceholderConfigurer propertyPlaceholder = new PropertySourcesPlaceholderConfigurer();
// setup properties that needs to be injected into the class under test
Properties properties = new Properties();
properties.setProperty("some.required.properties", "");
propertyPlaceholder.setProperties(properties);
return propertyPlaceholder;
}
}