My Spring Boot application is started like this:
new SpringApplicationBuilder()
.sources(ParentCtxConfig.class)
.child(ChildFirstCtxConfig.class)
.sibling(ChildSecondCtxConfig.class)
.run(args);
Config classes are annotated with @SpringBootApplication
. As a result, I have one root context and two child web contexts.
I want to write integration test and I would like to have same context hierarchy there. I want at least to test first child context (configured with ChildFirstCtxConfig.class
) with his parent context (ParentCtxConfig.class
). How can I achieve that?
Currently I autowired ApplicationContext
in my test so I can inspect it. I have this class annotation in the test:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { ParentCtxConfig.class, ChildFirstCtxConfig.class }, webEnvironment = WebEnvironment.RANDOM_PORT)
but that will produce single context and I want parent-child hierarchy.
I assume that I should annotate my test with @ContextHierarchy
annotation.
Changing my test annotation to this seems to work exactly the same like previous example:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = { ParentCtxConfig.class, ChildFirstCtxConfig.class })
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
But if I want to introduce @ContextHierarchy
and have something like this:
@RunWith(SpringRunner.class)
@ContextHierarchy({
@ContextConfiguration(name = "root", classes = ParentCtxConfig.class),
@ContextConfiguration(name = "child", classes = ChildFirstCtxConfig.class)
})
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
context is not started because of bean defined in parent context can't be found/autowired in child context. Setting loader = SpringBootContextLoader.class
doesn't help.
Sample code: GitHub
Update: This issue was fixed in Spring Boot 1.5.0 as mentioned by Peter Davis.
This is a limitation of @SpringBootTest
. Move accurately, it's a limitation of SpringBootContextLoader
. You could work around it by using a custom context loader that configures the parent context or with a ContextCustomizer
the factory for which would need to be listed in spring.factories
. Here's a rough example of the latter:
org.springframework.test.context.ContextCustomizerFactory=\
com.alex.demo.ctx.HierarchyContextCustomizerFactory
package com.alex.demo.ctx;
import java.util.List;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.MergedContextConfiguration;
public class HierarchyContextCustomizerFactory implements ContextCustomizerFactory {
@Override
public ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
return new ContextCustomizer() {
@Override
public void customizeContext(ConfigurableApplicationContext context,
MergedContextConfiguration mergedConfig) {
if (mergedConfig.getParentApplicationContext() != null) {
context.setParent(mergedConfig.getParentApplicationContext());
}
}
};
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With