Paul Tomlin opened SPR-10232 and commented
Overview
When using the TestContext framework it would be nice to easily be able to specify some PropertySources to be added to the Environment, in much the same way as @ActiveProfiles allows specifying the active profiles.
Ideally, something like below would result in a ResourcePropertySource being registered with the environment of the test's ApplicationContext prior to refresh:
@ContextConfiguration
@PropertySource("classpath:foo.properties")
public class MyTest {
// ...
}
Work-around
In Spring Framework 3.1, there is a workaround, but it's not nearly as tidy as the above proposal.
@ContextConfiguration(
locations = { ... },
loader = MyTest.CustomeContextLoader.class
)
public class MyTest {
public static class CustomContextLoader extends GenericXmlContextLoader {
@Override
protected void customizeContext(GenericApplicationContext context) {
// exception handling elided
context.getEnvironment()
.getPropertySources()
.addFirst(new ResourcePropertySource("classpath:foo.properties"));
}
}
}
There may be something in 3.2.x, probably as a result of #13650 and ApplicationContextInitializer, but ContextLoaderUtils doesn't seem to suggest so, and I'm not yet familiar enough to know.
Analysis
- As with
@ActiveProfiles, @PropertySource declarations on test classes should be inherited by default but overridable.
@PropertySource is not an @Inherited annotation, but AnnotationUtils.findAnnotationDeclaringClass() should take care of this.
- Inheritance and overriding behavior of
@PropertySource in integration tests must be consistent with the existing behavior in @Configuration classes.
- See code snippets from
ConfigurationClassPostProcessor and ConfigurationClassParser below.
- As far as possible, the existing business logic in
ConfigurationClassParser.processPropertySource() should be reused and not duplicated in the testing framework; in other words, consider extracting the existing logic into a static utility method or similar.
- The context cache key (i.e.,
MergedContextConfiguration) must take test property sources into account.
Relevant code from ConfigurationClassPostProcessor
// ...
// Handle any @PropertySource annotations
Stack<PropertySource<?>> parsedPropertySources = parser.getPropertySources();
if (!parsedPropertySources.isEmpty()) {
if (!(this.environment instanceof ConfigurableEnvironment)) {
logger.warn("Ignoring @PropertySource annotations. " +
"Reason: Environment must implement ConfigurableEnvironment");
}
else {
MutablePropertySources envPropertySources = ((ConfigurableEnvironment)this.environment).getPropertySources();
while (!parsedPropertySources.isEmpty()) {
envPropertySources.addLast(parsedPropertySources.pop());
}
}
}
// ...
Relevant code from ConfigurationClassParser
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
String[] locations = propertySource.getStringArray("value");
int nLocations = locations.length;
if (nLocations == 0) {
throw new IllegalArgumentException("At least one @PropertySource(value) location is required");
}
for (int i = 0; i < nLocations; i++) {
locations[i] = this.environment.resolveRequiredPlaceholders(locations[i]);
}
ClassLoader classLoader = this.resourceLoader.getClassLoader();
if (!StringUtils.hasText(name)) {
for (String location : locations) {
this.propertySources.push(new ResourcePropertySource(location, classLoader));
}
}
else {
if (nLocations == 1) {
this.propertySources.push(new ResourcePropertySource(name, locations[0], classLoader));
}
else {
CompositePropertySource ps = new CompositePropertySource(name);
for (String location : locations) {
ps.addPropertySource(new ResourcePropertySource(location, classLoader));
}
this.propertySources.push(ps);
}
}
}
Affects: 3.1 GA
Issue Links:
Referenced from: commits 3210041
4 votes, 8 watchers
Paul Tomlin opened SPR-10232 and commented
Overview
When using the TestContext framework it would be nice to easily be able to specify some
PropertySourcesto be added to theEnvironment, in much the same way as@ActiveProfilesallows specifying the active profiles.Ideally, something like below would result in a
ResourcePropertySourcebeing registered with the environment of the test'sApplicationContextprior to refresh:Work-around
In Spring Framework 3.1, there is a workaround, but it's not nearly as tidy as the above proposal.
There may be something in 3.2.x, probably as a result of #13650 and
ApplicationContextInitializer, butContextLoaderUtilsdoesn't seem to suggest so, and I'm not yet familiar enough to know.Analysis
@ActiveProfiles,@PropertySourcedeclarations on test classes should be inherited by default but overridable.@PropertySourceis not an@Inheritedannotation, butAnnotationUtils.findAnnotationDeclaringClass()should take care of this.@PropertySourcein integration tests must be consistent with the existing behavior in@Configurationclasses.ConfigurationClassPostProcessorandConfigurationClassParserbelow.ConfigurationClassParser.processPropertySource()should be reused and not duplicated in the testing framework; in other words, consider extracting the existing logic into a static utility method or similar.MergedContextConfiguration) must take test property sources into account.Relevant code from ConfigurationClassPostProcessor
Relevant code from ConfigurationClassParser
Affects: 3.1 GA
Issue Links:
@Configurationclasses@TestPropertySourcesupport in the TestContext framework ("is superseded by")Referenced from: commits 3210041
4 votes, 8 watchers