Reputation: 644
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?
Upvotes: 1
Views: 2796
Reputation: 31288
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)
Upvotes: 5