Commit 69b08291 authored by Andy Wilkinson's avatar Andy Wilkinson

Align precedence of @SpringBootTest properties with @TestPropertySource

This commit updates the precedence of properties configured using
@SpringBootTest to align with @TestPropertySource. Properties configured
using properties on @SpringBootTest are now added to the same property
source as those configured using properties on @TestPropertySource so
the precedence described in the javadoc of @TestPropertySource now
applies in full. Additionally, if both @TestPropertySource properties
and @SpringBootTest properties configure the same property, the value
from @TestPropertySource will win.

Closes gh-4828
parent 5f5db170
...@@ -19,10 +19,8 @@ package org.springframework.boot.test.context; ...@@ -19,10 +19,8 @@ package org.springframework.boot.test.context;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
...@@ -36,7 +34,6 @@ import org.springframework.context.ConfigurableApplicationContext; ...@@ -36,7 +34,6 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.SpringVersion; import org.springframework.core.SpringVersion;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.StandardEnvironment;
import org.springframework.test.context.ContextConfigurationAttributes; import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.ContextCustomizer;
...@@ -90,8 +87,6 @@ public class SpringBootContextLoader extends AbstractContextLoader { ...@@ -90,8 +87,6 @@ public class SpringBootContextLoader extends AbstractContextLoader {
if (!ObjectUtils.isEmpty(config.getActiveProfiles())) { if (!ObjectUtils.isEmpty(config.getActiveProfiles())) {
setActiveProfiles(environment, config.getActiveProfiles()); setActiveProfiles(environment, config.getActiveProfiles());
} }
Map<String, Object> properties = getEnvironmentProperties(config);
addProperties(environment, properties);
application.setEnvironment(environment); application.setEnvironment(environment);
List<ApplicationContextInitializer<?>> initializers = getInitializers(config, List<ApplicationContextInitializer<?>> initializers = getInitializers(config,
application); application);
...@@ -135,29 +130,19 @@ public class SpringBootContextLoader extends AbstractContextLoader { ...@@ -135,29 +130,19 @@ public class SpringBootContextLoader extends AbstractContextLoader {
+ StringUtils.arrayToCommaDelimitedString(profiles)); + StringUtils.arrayToCommaDelimitedString(profiles));
} }
protected Map<String, Object> getEnvironmentProperties( protected String[] getInlinedProperties(MergedContextConfiguration config) {
MergedContextConfiguration config) { ArrayList<String> properties = new ArrayList<String>();
Map<String, Object> properties = new LinkedHashMap<String, Object>();
// JMX bean names will clash if the same bean is used in multiple contexts // JMX bean names will clash if the same bean is used in multiple contexts
disableJmx(properties); disableJmx(properties);
properties.putAll(TestPropertySourceUtils properties.addAll(Arrays.asList(config.getPropertySourceProperties()));
.convertInlinedPropertiesToMap(config.getPropertySourceProperties()));
if (!isEmbeddedWebEnvironment(config)) { if (!isEmbeddedWebEnvironment(config)) {
properties.put("server.port", "-1"); properties.add("server.port=-1");
} }
return properties; return properties.toArray(new String[properties.size()]);
} }
private void disableJmx(Map<String, Object> properties) { private void disableJmx(List<String> properties) {
properties.put("spring.jmx.enabled", "false"); properties.add("spring.jmx.enabled=false");
}
private void addProperties(ConfigurableEnvironment environment,
Map<String, Object> properties) {
// @IntegrationTest properties go before external configuration and after system
environment.getPropertySources().addAfter(
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
new MapPropertySource("integrationTest", properties));
} }
private List<ApplicationContextInitializer<?>> getInitializers( private List<ApplicationContextInitializer<?>> getInitializers(
...@@ -166,8 +151,8 @@ public class SpringBootContextLoader extends AbstractContextLoader { ...@@ -166,8 +151,8 @@ public class SpringBootContextLoader extends AbstractContextLoader {
for (ContextCustomizer contextCustomizer : config.getContextCustomizers()) { for (ContextCustomizer contextCustomizer : config.getContextCustomizers()) {
initializers.add(new ContextCustomizerAdapter(contextCustomizer, config)); initializers.add(new ContextCustomizerAdapter(contextCustomizer, config));
} }
initializers.add(new PropertySourceLocationsInitializer( initializers.add(new TestPropertySourcesInitializer(
config.getPropertySourceLocations())); config.getPropertySourceLocations(), getInlinedProperties(config)));
initializers.addAll(application.getInitializers()); initializers.addAll(application.getInitializers());
for (Class<? extends ApplicationContextInitializer<?>> initializerClass : config for (Class<? extends ApplicationContextInitializer<?>> initializerClass : config
.getContextInitializerClasses()) { .getContextInitializerClasses()) {
...@@ -258,21 +243,27 @@ public class SpringBootContextLoader extends AbstractContextLoader { ...@@ -258,21 +243,27 @@ public class SpringBootContextLoader extends AbstractContextLoader {
} }
/** /**
* {@link ApplicationContextInitializer} to setup test property source locations. * {@link ApplicationContextInitializer} to set up test property sources.
*/ */
private static class PropertySourceLocationsInitializer private static class TestPropertySourcesInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> { implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private final String[] propertySourceLocations; private final String[] propertySourceLocations;
PropertySourceLocationsInitializer(String[] propertySourceLocations) { private final String[] inlinedProperties;
TestPropertySourcesInitializer(String[] propertySourceLocations,
String[] inlinedProperties) {
this.propertySourceLocations = propertySourceLocations; this.propertySourceLocations = propertySourceLocations;
this.inlinedProperties = inlinedProperties;
} }
@Override @Override
public void initialize(ConfigurableApplicationContext applicationContext) { public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertySourceUtils.addPropertiesFilesToEnvironment(applicationContext, TestPropertySourceUtils.addPropertiesFilesToEnvironment(applicationContext,
this.propertySourceLocations); this.propertySourceLocations);
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(applicationContext,
this.inlinedProperties);
} }
} }
......
...@@ -206,7 +206,9 @@ public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstr ...@@ -206,7 +206,9 @@ public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstr
Class<?> testClass = mergedConfig.getTestClass(); Class<?> testClass = mergedConfig.getTestClass();
String[] properties = getProperties(testClass); String[] properties = getProperties(testClass);
if (!ObjectUtils.isEmpty(properties)) { if (!ObjectUtils.isEmpty(properties)) {
propertySourceProperties.addAll(Arrays.asList(properties)); // Added first so that inlined properties from @TestPropertySource take
// precedence
propertySourceProperties.addAll(0, Arrays.asList(properties));
} }
if (getWebEnvironment(testClass) == WebEnvironment.RANDOM_PORT) { if (getWebEnvironment(testClass) == WebEnvironment.RANDOM_PORT) {
propertySourceProperties.add("server.port=0"); propertySourceProperties.add("server.port=0");
......
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
package org.springframework.boot.test.context; package org.springframework.boot.test.context;
import javax.annotation.PostConstruct;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -27,7 +25,6 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; ...@@ -27,7 +25,6 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
...@@ -35,45 +32,76 @@ import org.springframework.test.context.junit4.SpringRunner; ...@@ -35,45 +32,76 @@ import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Tests for {@link SpringBootTest} with {@link TestPropertySource} locations. * Tests for using {@link SpringBootTest} with {@link TestPropertySource}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@DirtiesContext @DirtiesContext
@SpringBootTest(webEnvironment = WebEnvironment.NONE, properties = "value1=123") @SpringBootTest(webEnvironment = WebEnvironment.NONE, properties = {
@TestPropertySource(properties = "value2=456", locations = "classpath:/test-property-source-annotation.properties") "boot-test-inlined=foo", "b=boot-test-inlined", "c=boot-test-inlined" })
public class SpringBootTestPropertyLocationTests { @TestPropertySource(properties = { "property-source-inlined=bar",
"a=property-source-inlined",
"c=property-source-inlined" }, locations = "classpath:/test-property-source-annotation.properties")
public class SpringBootTestWithTestPropertySourceTests {
@Autowired @Autowired
private Environment environment; private Config config;
@Test
public void propertyFromSpringBootTestProperties() {
assertThat(this.config.bootTestInlined).isEqualTo("foo");
}
@Test
public void propertyFromTestPropertySourceProperties() {
assertThat(this.config.propertySourceInlined).isEqualTo("bar");
}
@Test
public void propertyFromTestPropertySourceLocations() {
assertThat(this.config.propertySourceLocation).isEqualTo("baz");
}
@Test @Test
public void loadedProperties() throws Exception { public void propertyFromPropertySourcePropertiesOverridesPropertyFromPropertySourceLocations() {
assertThat(this.environment.getProperty("value1")).isEqualTo("123"); assertThat(this.config.propertySourceInlinedOverridesPropertySourceLocation)
assertThat(this.environment.getProperty("value2")).isEqualTo("456"); .isEqualTo("property-source-inlined");
assertThat(this.environment.getProperty("annotation-referenced")) }
.isEqualTo("fromfile");
@Test
public void propertyFromBootTestPropertiesOverridesPropertyFromPropertySourceLocations() {
assertThat(this.config.bootTestInlinedOverridesPropertySourceLocation)
.isEqualTo("boot-test-inlined");
}
@Test
public void propertyFromPropertySourcePropertiesOverridesPropertyFromBootTestProperties() {
assertThat(this.config.propertySourceInlinedOverridesBootTestInlined)
.isEqualTo("property-source-inlined");
} }
@Configuration @Configuration
static class Config { static class Config {
@Value("${value1}") @Value("${boot-test-inlined}")
private String value1; private String bootTestInlined;
@Value("${value2}") @Value("${property-source-inlined}")
private String value2; private String propertySourceInlined;
@Value("${annotation-referenced}") @Value("${property-source-location}")
private String annotationReferenced; private String propertySourceLocation;
@PostConstruct @Value("${a}")
void checkValues() { private String propertySourceInlinedOverridesPropertySourceLocation;
assertThat(this.value1).isEqualTo("123");
assertThat(this.value2).isEqualTo("456"); @Value("${b}")
assertThat(this.annotationReferenced).isEqualTo("fromfile"); private String bootTestInlinedOverridesPropertySourceLocation;
}
@Value("${c}")
private String propertySourceInlinedOverridesBootTestInlined;
@Bean @Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholder() { public static PropertySourcesPlaceholderConfigurer propertyPlaceholder() {
......
annotation-referenced=fromfile property-source-location=baz
a=property-source-location
b=property-source-location
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment