diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java
index 0212338e156..7719589b8b6 100644
--- a/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java
+++ b/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java
@@ -53,22 +53,19 @@ import org.springframework.context.ConfigurableApplicationContext;
* The term annotated class can refer to any of the following.
*
*
- * - A class annotated with @{@link org.springframework.context.annotation.Configuration
- * Configuration}
+ * - A class annotated with {@link org.springframework.context.annotation.Configuration @Configuration}
* - A component (i.e., a class annotated with
* {@link org.springframework.stereotype.Component @Component},
* {@link org.springframework.stereotype.Service @Service},
* {@link org.springframework.stereotype.Repository @Repository}, etc.)
* - A JSR-330 compliant class that is annotated with {@code javax.inject} annotations
- * - Any other class that contains {@link org.springframework.context.annotation.Bean
- * @Bean}-methods
+ * - Any other class that contains {@link org.springframework.context.annotation.Bean @Bean}-methods
*
*
*
- * Consult the Javadoc for {@link org.springframework.context.annotation.Configuration
- * @Configuration} and {@link org.springframework.context.annotation.Bean @Bean} for
- * further information regarding the configuration and semantics of
- * annotated classes.
+ * Consult the Javadoc for {@link org.springframework.context.annotation.Configuration @Configuration}
+ * and {@link org.springframework.context.annotation.Bean @Bean} for further
+ * information regarding the configuration and semantics of annotated classes.
*
*
* As of Spring Framework 4.0, this annotation may be used as a meta-annotation
@@ -78,6 +75,7 @@ import org.springframework.context.ConfigurableApplicationContext;
* @since 2.5
* @see ContextHierarchy
* @see ActiveProfiles
+ * @see TestPropertySource
* @see ContextLoader
* @see SmartContextLoader
* @see ContextConfigurationAttributes
diff --git a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java
index 5dc0f20cff4..a6f6ac01b30 100644
--- a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java
+++ b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,15 +34,17 @@ import org.springframework.util.StringUtils;
/**
* {@code MergedContextConfiguration} encapsulates the merged
* context configuration declared on a test class and all of its superclasses
- * via {@link ContextConfiguration @ContextConfiguration} and
- * {@link ActiveProfiles @ActiveProfiles}.
+ * via {@link ContextConfiguration @ContextConfiguration},
+ * {@link ActiveProfiles @ActiveProfiles}, and
+ * {@link TestPropertySource @TestPropertySource}.
*
- *
Merged resource locations, annotated classes, and active profiles
- * represent all declared values in the test class hierarchy taking into
- * consideration the semantics of the
- * {@link ContextConfiguration#inheritLocations inheritLocations} and
- * {@link ActiveProfiles#inheritProfiles inheritProfiles} flags in
- * {@code @ContextConfiguration} and {@code @ActiveProfiles}, respectively.
+ *
Merged context resource locations, annotated classes, active profiles,
+ * property resource locations, and in-lined properties represent all declared
+ * values in the test class hierarchy taking into consideration the semantics
+ * of the {@link ContextConfiguration#inheritLocations},
+ * {@link ActiveProfiles#inheritProfiles},
+ * {@link TestPropertySource#inheritLocations}, and
+ * {@link TestPropertySource#inheritProperties} flags.
*
*
A {@link SmartContextLoader} uses {@code MergedContextConfiguration}
* to load an {@link org.springframework.context.ApplicationContext ApplicationContext}.
@@ -73,13 +75,15 @@ public class MergedContextConfiguration implements Serializable {
private final Class>[] classes;
private final Set>> contextInitializerClasses;
private final String[] activeProfiles;
+ private final String[] propertySourceLocations;
+ private final String[] propertySourceProperties;
private final ContextLoader contextLoader;
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
private final MergedContextConfiguration parent;
- private static String[] processLocations(String[] locations) {
- return locations == null ? EMPTY_STRING_ARRAY : locations;
+ private static String[] processStrings(String[] array) {
+ return array == null ? EMPTY_STRING_ARRAY : array;
}
private static Class>[] processClasses(Class>[] classes) {
@@ -115,20 +119,15 @@ public class MergedContextConfiguration implements Serializable {
/**
* Create a new {@code MergedContextConfiguration} instance for the
- * supplied test class, resource locations, annotated classes, active
- * profiles, and {@code ContextLoader}.
- *
- * If a {@code null} value is supplied for {@code locations},
- * {@code classes}, or {@code activeProfiles} an empty array will
- * be stored instead. Furthermore, active profiles will be sorted, and duplicate
- * profiles will be removed.
+ * supplied parameters.
+ *
Delegates to
+ * {@link #MergedContextConfiguration(Class, String[], Class[], Set, String[], String[], String[], ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}.
*
* @param testClass the test class for which the configuration was merged
- * @param locations the merged resource locations
+ * @param locations the merged context resource locations
* @param classes the merged annotated classes
* @param activeProfiles the merged active bean definition profiles
* @param contextLoader the resolved {@code ContextLoader}
- * @see #MergedContextConfiguration(Class, String[], Class[], Set, String[], ContextLoader)
*/
public MergedContextConfiguration(Class> testClass, String[] locations, Class>[] classes,
String[] activeProfiles, ContextLoader contextLoader) {
@@ -137,18 +136,12 @@ public class MergedContextConfiguration implements Serializable {
/**
* Create a new {@code MergedContextConfiguration} instance for the
- * supplied test class, resource locations, annotated classes, context
- * initializers, active profiles, and {@code ContextLoader}.
- *
- *
If a {@code null} value is supplied for {@code locations},
- * {@code classes}, or {@code activeProfiles} an empty array will
- * be stored instead. If a {@code null} value is supplied for the
- * {@code contextInitializerClasses} an empty set will be stored instead.
- * Furthermore, active profiles will be sorted, and duplicate profiles will
- * be removed.
+ * supplied parameters.
+ *
Delegates to
+ * {@link #MergedContextConfiguration(Class, String[], Class[], Set, String[], String[], String[], ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}.
*
* @param testClass the test class for which the configuration was merged
- * @param locations the merged resource locations
+ * @param locations the merged context resource locations
* @param classes the merged annotated classes
* @param contextInitializerClasses the merged context initializer classes
* @param activeProfiles the merged active bean definition profiles
@@ -166,19 +159,12 @@ public class MergedContextConfiguration implements Serializable {
/**
* Create a new {@code MergedContextConfiguration} instance for the
- * supplied test class, resource locations, annotated classes, context
- * initializers, active profiles, {@code ContextLoader}, and parent
- * configuration.
- *
- *
If a {@code null} value is supplied for {@code locations},
- * {@code classes}, or {@code activeProfiles} an empty array will
- * be stored instead. If a {@code null} value is supplied for the
- * {@code contextInitializerClasses} an empty set will be stored instead.
- * Furthermore, active profiles will be sorted, and duplicate profiles will
- * be removed.
+ * supplied parameters.
+ *
Delegates to
+ * {@link #MergedContextConfiguration(Class, String[], Class[], Set, String[], String[], String[], ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}.
*
* @param testClass the test class for which the configuration was merged
- * @param locations the merged resource locations
+ * @param locations the merged context resource locations
* @param classes the merged annotated classes
* @param contextInitializerClasses the merged context initializer classes
* @param activeProfiles the merged active bean definition profiles
@@ -195,11 +181,50 @@ public class MergedContextConfiguration implements Serializable {
Set>> contextInitializerClasses,
String[] activeProfiles, ContextLoader contextLoader,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
+ this(testClass, locations, classes, contextInitializerClasses, activeProfiles, null, null, contextLoader,
+ cacheAwareContextLoaderDelegate, parent);
+ }
+
+ /**
+ * Create a new {@code MergedContextConfiguration} instance for the
+ * supplied parameters.
+ *
+ * If a {@code null} value is supplied for {@code locations},
+ * {@code classes}, {@code activeProfiles}, {@code propertySourceLocations},
+ * or {@code propertySourceProperties} an empty array will be stored instead.
+ * If a {@code null} value is supplied for the
+ * {@code contextInitializerClasses} an empty set will be stored instead.
+ * Furthermore, active profiles will be sorted, and duplicate profiles
+ * will be removed.
+ *
+ * @param testClass the test class for which the configuration was merged
+ * @param locations the merged context resource locations
+ * @param classes the merged annotated classes
+ * @param contextInitializerClasses the merged context initializer classes
+ * @param activeProfiles the merged active bean definition profiles
+ * @param propertySourceLocations the merged {@code PropertySource} locations
+ * @param propertySourceProperties the merged {@code PropertySource} properties
+ * @param contextLoader the resolved {@code ContextLoader}
+ * @param cacheAwareContextLoaderDelegate a cache-aware context loader
+ * delegate with which to retrieve the parent context
+ * @param parent the parent configuration or {@code null} if there is no parent
+ * @since 4.1
+ */
+ public MergedContextConfiguration(
+ Class> testClass,
+ String[] locations,
+ Class>[] classes,
+ Set>> contextInitializerClasses,
+ String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
+ ContextLoader contextLoader, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
+ MergedContextConfiguration parent) {
this.testClass = testClass;
- this.locations = processLocations(locations);
+ this.locations = processStrings(locations);
this.classes = processClasses(classes);
this.contextInitializerClasses = processContextInitializerClasses(contextInitializerClasses);
this.activeProfiles = processActiveProfiles(activeProfiles);
+ this.propertySourceLocations = processStrings(propertySourceLocations);
+ this.propertySourceProperties = processStrings(propertySourceProperties);
this.contextLoader = contextLoader;
this.cacheAwareContextLoaderDelegate = cacheAwareContextLoaderDelegate;
this.parent = parent;
@@ -213,7 +238,10 @@ public class MergedContextConfiguration implements Serializable {
}
/**
- * Get the merged resource locations for the {@linkplain #getTestClass() test class}.
+ * Get the merged resource locations for {@code ApplicationContext}
+ * configuration files for the {@linkplain #getTestClass() test class}.
+ * Context resource locations typically represent XML configuration
+ * files or Groovy scripts.
*/
public String[] getLocations() {
return locations;
@@ -228,7 +256,7 @@ public class MergedContextConfiguration implements Serializable {
/**
* Determine if this {@code MergedContextConfiguration} instance has
- * path-based resource locations.
+ * path-based context resource locations.
*
* @return {@code true} if the {@link #getLocations() locations} array is not empty
* @since 4.0.4
@@ -254,7 +282,7 @@ public class MergedContextConfiguration implements Serializable {
/**
* Determine if this {@code MergedContextConfiguration} instance has
- * either path-based resource locations or class-based resources.
+ * either path-based context resource locations or class-based resources.
*
* @return {@code true} if either the {@link #getLocations() locations}
* or the {@link #getClasses() classes} array is not empty
@@ -275,12 +303,36 @@ public class MergedContextConfiguration implements Serializable {
}
/**
- * Get the merged active bean definition profiles for the {@linkplain #getTestClass() test class}.
+ * Get the merged active bean definition profiles for the
+ * {@linkplain #getTestClass() test class}.
+ * @see ActiveProfiles
*/
public String[] getActiveProfiles() {
return activeProfiles;
}
+ /**
+ * Get the merged resource locations for test {@code PropertySources} for the
+ * {@linkplain #getTestClass() test class}.
+ * @see TestPropertySource#locations
+ * @see java.util.Properties
+ */
+ public String[] getPropertySourceLocations() {
+ return propertySourceLocations;
+ }
+
+ /**
+ * Get the merged test {@code PropertySource} properties for the
+ * {@linkplain #getTestClass() test class}.
+ *
Properties will be loaded into the {@code Environment}'s set of
+ * {@code PropertySources}.
+ * @see TestPropertySource#properties
+ * @see java.util.Properties
+ */
+ public String[] getPropertySourceProperties() {
+ return propertySourceProperties;
+ }
+
/**
* Get the resolved {@link ContextLoader} for the {@linkplain #getTestClass() test class}.
*/
@@ -334,6 +386,8 @@ public class MergedContextConfiguration implements Serializable {
result = prime * result + Arrays.hashCode(classes);
result = prime * result + contextInitializerClasses.hashCode();
result = prime * result + Arrays.hashCode(activeProfiles);
+ result = prime * result + Arrays.hashCode(propertySourceLocations);
+ result = prime * result + Arrays.hashCode(propertySourceProperties);
result = prime * result + (parent == null ? 0 : parent.hashCode());
result = prime * result + nullSafeToString(contextLoader).hashCode();
return result;
@@ -345,6 +399,8 @@ public class MergedContextConfiguration implements Serializable {
* {@linkplain #getClasses() annotated classes},
* {@linkplain #getContextInitializerClasses() context initializer classes},
* {@linkplain #getActiveProfiles() active profiles},
+ * {@linkplain #getPropertySourceLocations() property source locations},
+ * {@linkplain #getPropertySourceProperties() property source properties},
* {@linkplain #getParent() parents}, and the fully qualified names of their
* {@link #getContextLoader() ContextLoaders}.
*/
@@ -376,6 +432,14 @@ public class MergedContextConfiguration implements Serializable {
return false;
}
+ if (!Arrays.equals(this.propertySourceLocations, that.propertySourceLocations)) {
+ return false;
+ }
+
+ if (!Arrays.equals(this.propertySourceProperties, that.propertySourceProperties)) {
+ return false;
+ }
+
if (this.parent == null) {
if (that.parent != null) {
return false;
@@ -396,8 +460,10 @@ public class MergedContextConfiguration implements Serializable {
* Provide a String representation of the {@linkplain #getTestClass() test class},
* {@linkplain #getLocations() locations}, {@linkplain #getClasses() annotated classes},
* {@linkplain #getContextInitializerClasses() context initializer classes},
- * {@linkplain #getActiveProfiles() active profiles}, the name of the
- * {@link #getContextLoader() ContextLoader}, and the
+ * {@linkplain #getActiveProfiles() active profiles},
+ * {@linkplain #getPropertySourceLocations() property source locations},
+ * {@linkplain #getPropertySourceProperties() property source properties},
+ * the name of the {@link #getContextLoader() ContextLoader}, and the
* {@linkplain #getParent() parent configuration}.
*/
@Override
@@ -408,6 +474,8 @@ public class MergedContextConfiguration implements Serializable {
.append("classes", ObjectUtils.nullSafeToString(classes))//
.append("contextInitializerClasses", ObjectUtils.nullSafeToString(contextInitializerClasses))//
.append("activeProfiles", ObjectUtils.nullSafeToString(activeProfiles))//
+ .append("propertySourceLocations", ObjectUtils.nullSafeToString(propertySourceLocations))//
+ .append("propertySourceProperties", ObjectUtils.nullSafeToString(propertySourceProperties))//
.append("contextLoader", nullSafeToString(contextLoader))//
.append("parent", parent)//
.toString();
diff --git a/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java b/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java
new file mode 100644
index 00000000000..281994544ec
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * {@code @TestPropertySource} is a class-level annotation that is used to
+ * configure the {@link #locations} of properties files and inlined
+ * {@link #properties} to be added to the {@code Environment}'s set of
+ * {@code PropertySources} for an
+ * {@link org.springframework.context.ApplicationContext ApplicationContext}
+ * for integration tests.
+ *
+ *
Precedence
+ * Test property sources have higher precedence than those loaded from the
+ * operating system's environment or Java system properties as well as property
+ * sources added by the application declaratively via
+ * {@link org.springframework.context.annotation.PropertySource @PropertySource}
+ * or programmatically (e.g., via an
+ * {@link org.springframework.context.ApplicationContextInitializer ApplicationContextInitializer}
+ * or some other means). Thus, test property sources can be used to selectively
+ * override properties defined in system and application property sources.
+ * Furthermore, inlined {@link #properties} have higher precedence than
+ * properties loaded from resource {@link #locations}.
+ *
+ *
Default Properties File Detection
+ * If {@code @TestPropertySource} is declared as an empty annotation
+ * (i.e., without explicit values for {@link #locations} or {@link #properties}),
+ * an attempt will be made to detect a default properties file relative
+ * to the class that declared the annotation. For example, if the annotated test
+ * class is {@code com.example.MyTest}, the corresponding default properties file
+ * is {@code "classpath:com/example/MyTest.properties"}. If the default cannot be
+ * detected, an {@link IllegalStateException} will be thrown.
+ *
+ *
Enabling @TestPropertySource
+ * {@code @TestPropertySource} is enabled if the configured
+ * {@linkplain ContextConfiguration#loader context loader} honors it. Every
+ * {@code SmartContextLoader} that is a subclass of either
+ * {@link org.springframework.test.context.support.AbstractGenericContextLoader AbstractGenericContextLoader} or
+ * {@link org.springframework.test.context.web.AbstractGenericWebContextLoader AbstractGenericWebContextLoader}
+ * provides automatic support for {@code @TestPropertySource}, and this includes
+ * every {@code SmartContextLoader} provided by the Spring TestContext Framework.
+ *
+ *
Miscellaneous
+ *
+ * - Typically, {@code @TestPropertySource} will be used in conjunction with
+ * {@link ContextConfiguration @ContextConfiguration}.
+ * - This annotation may be used as a meta-annotation to create
+ * custom composed annotations; however, caution should be taken if
+ * this annotation and {@code @ContextConfiguration} are combined on a composed
+ * annotation since the {@code locations} and {@code inheritLocations} attributes
+ * of both annotations can lead to ambiguity during the attribute resolution
+ * process.
+ *
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ * @see ContextConfiguration
+ * @see org.springframework.core.env.Environment
+ * @see org.springframework.core.env.PropertySource
+ * @see org.springframework.context.annotation.PropertySource
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface TestPropertySource {
+
+ /**
+ * Alias for {@link #locations}.
+ *
+ * This attribute may not be used in conjunction with
+ * {@link #locations}, but it may be used instead of {@link #locations}.
+ *
+ * @see #locations
+ */
+ String[] value() default {};
+
+ /**
+ * The resource locations of properties files to be loaded into the
+ * {@code Environment}'s set of {@code PropertySources}. Each location
+ * will be added to the enclosing {@code Environment} as its own property
+ * source, in the order declared.
+ *
+ *
Supported File Formats
+ * Both traditional and XML-based properties file formats are supported
+ * — for example, {@code "classpath:/com/example/test.properties"}
+ * or {@code "file:/path/to/file.xml"}.
+ *
+ *
Path Resource Semantics
+ * Each path will be interpreted as a Spring
+ * {@link org.springframework.core.io.Resource Resource}. A plain path
+ * — for example, {@code "test.properties"} — will be treated as a
+ * classpath resource that is relative to the package in which the
+ * test class is defined. A path starting with a slash will be treated as an
+ * absolute classpath resource, for example:
+ * {@code "/org/example/test.xml"}. A path which references a
+ * URL (e.g., a path prefixed with
+ * {@link org.springframework.util.ResourceUtils#CLASSPATH_URL_PREFIX classpath:},
+ * {@link org.springframework.util.ResourceUtils#FILE_URL_PREFIX file:},
+ * {@code http:}, etc.) will be loaded using the specified resource protocol.
+ * Resource location wildcards (e.g. **/*.properties)
+ * are not permitted: each location must evaluate to exactly one
+ * {@code .properties} or {@code .xml} resource.
+ *
+ *
Default Properties File Detection
+ * See the class-level Javadoc for a discussion on detection of defaults.
+ *
+ *
Precedence
+ * Properties loaded from resource locations have lower precedence than
+ * inlined {@link #properties}.
+ *
+ *
This attribute may not be used in conjunction with
+ * {@link #value}, but it may be used instead of {@link #value}.
+ *
+ * @see #inheritLocations
+ * @see #value
+ * @see #properties
+ * @see org.springframework.core.env.PropertySource
+ */
+ String[] locations() default {};
+
+ /**
+ * Whether or not test property source {@link #locations} from superclasses
+ * should be inherited.
+ *
+ *
The default value is {@code true}, which means that a test class will
+ * inherit property source locations defined by a superclass.
+ * Specifically, the property source locations for a test class will be
+ * appended to the list of property source locations defined by a superclass.
+ * Thus, subclasses have the option of extending the list of test
+ * property source locations.
+ *
+ *
If {@code inheritLocations} is set to {@code false}, the property
+ * source locations for the test class will shadow and effectively
+ * replace any property source locations defined by a superclass.
+ *
+ *
In the following example, the {@code ApplicationContext} for
+ * {@code BaseTest} will be loaded using only the {@code "base.properties"}
+ * file as a test property source. In contrast, the {@code ApplicationContext}
+ * for {@code ExtendedTest} will be loaded using the {@code "base.properties"}
+ * and {@code "extended.properties"} files as test property
+ * source locations.
+ *
+ * @TestPropertySource("base.properties")
+ * @ContextConfiguration
+ * public class BaseTest {
+ * // ...
+ * }
+ *
+ * @TestPropertySource("extended.properties")
+ * @ContextConfiguration
+ * public class ExtendedTest extends BaseTest {
+ * // ...
+ * }
+ *
+ *
+ * @see #locations
+ */
+ boolean inheritLocations() default true;
+
+ /**
+ * Inlined properties in the form of key-value pairs that
+ * should be added to the Spring
+ * {@link org.springframework.core.env.Environment Environment} before the
+ * {@code ApplicationContext} is loaded for the test. All key-value pairs
+ * will be added to the enclosing {@code Environment} as a single test
+ * {@code PropertySource} with the highest precedence.
+ *
+ * Supported Syntax
+ * The supported syntax for key-value pairs is the same as the
+ * syntax defined for entries in a Java
+ * {@linkplain java.util.Properties#load(java.io.Reader) properties file}:
+ *
+ * - {@code "key=value"}
+ * - {@code "key:value"}
+ * - {@code "key value"}
+ *
+ *
+ * Precedence
+ * Properties declared via this attribute have higher precedence than
+ * properties loaded from resource {@link locations}.
+ *
+ *
This attribute may be used in conjunction with {@link #value}
+ * or {@link #locations}.
+ *
+ * @see #inheritProperties
+ * @see #locations
+ * @see org.springframework.core.env.PropertySource
+ */
+ String[] properties() default {};
+
+ /**
+ * Whether or not inlined test {@link #properties} from superclasses should
+ * be inherited.
+ *
+ *
The default value is {@code true}, which means that a test class will
+ * inherit inlined properties defined by a superclass. Specifically,
+ * the inlined properties for a test class will be appended to the list of
+ * inlined properties defined by a superclass. Thus, subclasses have the
+ * option of extending the list of inlined test properties.
+ *
+ *
If {@code inheritProperties} is set to {@code false}, the inlined
+ * properties for the test class will shadow and effectively
+ * replace any inlined properties defined by a superclass.
+ *
+ *
In the following example, the {@code ApplicationContext} for
+ * {@code BaseTest} will be loaded using only the inlined {@code key1}
+ * property. In contrast, the {@code ApplicationContext} for
+ * {@code ExtendedTest} will be loaded using the inlined {@code key1}
+ * and {@code key2} properties.
+ *
+ * @TestPropertySource(properties = "key1 = value1")
+ * @ContextConfiguration
+ * public class BaseTest {
+ * // ...
+ * }
+ *
+ * @TestPropertySource(properties = "key2 = value2")
+ * @ContextConfiguration
+ * public class ExtendedTest extends BaseTest {
+ * // ...
+ * }
+ *
+ *
+ * @see #properties
+ */
+ boolean inheritProperties() default true;
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java
index 23181b8a886..9bf45de085a 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java
@@ -16,8 +16,13 @@
package org.springframework.test.context.support;
+import java.io.IOException;
+import java.io.StringReader;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Properties;
import java.util.Set;
import org.apache.commons.logging.Log;
@@ -28,7 +33,12 @@ import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.MapPropertySource;
+import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
@@ -53,6 +63,7 @@ import org.springframework.util.ResourceUtils;
*
* @author Sam Brannen
* @author Juergen Hoeller
+ * @author Dave Syer
* @since 2.5
* @see #generateDefaultLocations
* @see #getResourceSuffixes
@@ -62,6 +73,8 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
+ private static final String LINE_SEPARATOR = System.getProperty("line.separator");
+
private static final Log logger = LogFactory.getLog(AbstractContextLoader.class);
@@ -96,15 +109,24 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
*
* - Sets the active bean definition profiles from the supplied
* {@code MergedContextConfiguration} in the
- * {@link org.springframework.core.env.Environment Environment} of the context.
+ * {@link org.springframework.core.env.Environment Environment} of the
+ * context.
+ * - Adds {@link PropertySource PropertySources} for all
+ * {@linkplain MergedContextConfiguration#getPropertySourceLocations()
+ * resource locations} and
+ * {@linkplain MergedContextConfiguration#getPropertySourceProperties()
+ * inlined properties} from the supplied {@code MergedContextConfiguration}
+ * to the {@code Environment} of the context.
* - Determines what (if any) context initializer classes have been supplied
- * via the {@code MergedContextConfiguration} and
- * {@linkplain ApplicationContextInitializer#initialize invokes each} with the
+ * via the {@code MergedContextConfiguration} and instantiates and
+ * {@linkplain ApplicationContextInitializer#initialize invokes} each with the
* given application context.
+ *
+ * - Any {@code ApplicationContextInitializers} implementing
+ * {@link org.springframework.core.Ordered Ordered} or annotated with {@link
+ * org.springframework.core.annotation.Order @Order} will be sorted appropriately.
+ *
*
- * Any {@code ApplicationContextInitializers} implementing
- * {@link org.springframework.core.Ordered Ordered} or marked with {@link
- * org.springframework.core.annotation.Order @Order} will be sorted appropriately.
* @param context the newly created application context
* @param mergedConfig the merged context configuration
* @since 3.2
@@ -112,10 +134,79 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
* @see #loadContext(MergedContextConfiguration)
* @see ConfigurableApplicationContext#setId
*/
- @SuppressWarnings("unchecked")
protected void prepareContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
context.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles());
+ addResourcePropertySourcesToEnvironment(context, mergedConfig);
+ addInlinedPropertiesToEnvironment(context, mergedConfig);
+ invokeApplicationContextInitializers(context, mergedConfig);
+ }
+
+ /**
+ * @since 4.1
+ */
+ private void addResourcePropertySourcesToEnvironment(ConfigurableApplicationContext context,
+ MergedContextConfiguration mergedConfig) {
+ try {
+ ConfigurableEnvironment environment = context.getEnvironment();
+ String[] locations = mergedConfig.getPropertySourceLocations();
+ for (String location : locations) {
+ String resolvedLocation = environment.resolveRequiredPlaceholders(location);
+ Resource resource = context.getResource(resolvedLocation);
+ ResourcePropertySource ps = new ResourcePropertySource(resource);
+ environment.getPropertySources().addFirst(ps);
+ }
+ }
+ catch (IOException e) {
+ throw new IllegalStateException("Failed to add PropertySource to Environment", e);
+ }
+ }
+
+ /**
+ * @since 4.1
+ */
+ private void addInlinedPropertiesToEnvironment(ConfigurableApplicationContext context,
+ MergedContextConfiguration mergedConfig) {
+ String[] keyValuePairs = mergedConfig.getPropertySourceProperties();
+ if (!ObjectUtils.isEmpty(keyValuePairs)) {
+ String name = "test properties " + ObjectUtils.nullSafeToString(keyValuePairs);
+ MapPropertySource ps = new MapPropertySource(name, extractEnvironmentProperties(keyValuePairs));
+ context.getEnvironment().getPropertySources().addFirst(ps);
+ }
+ }
+ /**
+ * Extract environment properties from the supplied key/value pairs.
+ *
Parsing of the key/value pairs is achieved by converting all pairs
+ * into a single virtual properties file in memory and delegating
+ * to {@link Properties#load(java.io.Reader)} to parse that virtual file.
+ *
This code has been adapted from Spring Boot's
+ * {@link org.springframework.boot.test.SpringApplicationContextLoader SpringApplicationContextLoader}.
+ * @since 4.1
+ */
+ private Map extractEnvironmentProperties(String[] keyValuePairs) {
+ StringBuilder sb = new StringBuilder();
+ for (String keyValuePair : keyValuePairs) {
+ sb.append(keyValuePair).append(LINE_SEPARATOR);
+ }
+ String content = sb.toString();
+ Properties props = new Properties();
+ try {
+ props.load(new StringReader(content));
+ }
+ catch (IOException e) {
+ throw new IllegalStateException("Failed to load test environment properties from: " + content, e);
+ }
+
+ Map properties = new HashMap();
+ for (String name : props.stringPropertyNames()) {
+ properties.put(name, props.getProperty(name));
+ }
+ return properties;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void invokeApplicationContextInitializers(ConfigurableApplicationContext context,
+ MergedContextConfiguration mergedConfig) {
Set>> initializerClasses = mergedConfig.getContextInitializerClasses();
if (initializerClasses.isEmpty()) {
// no ApplicationContextInitializers have been declared -> nothing to do
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
index cfdca3b7af4..cf2b9f7a146 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
@@ -26,7 +26,6 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContextInitializer;
@@ -100,13 +99,14 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
Class annotationType = TestExecutionListeners.class;
List> classesList = new ArrayList>();
- AnnotationDescriptor descriptor =
- MetaAnnotationUtils.findAnnotationDescriptor(clazz, annotationType);
+ AnnotationDescriptor descriptor = MetaAnnotationUtils.findAnnotationDescriptor(clazz,
+ annotationType);
// Use defaults?
if (descriptor == null) {
if (logger.isDebugEnabled()) {
- logger.debug("@TestExecutionListeners is not present for class [" + clazz.getName() + "]: using defaults.");
+ logger.debug("@TestExecutionListeners is not present for class [" + clazz.getName()
+ + "]: using defaults.");
}
classesList.addAll(getDefaultTestExecutionListenerClasses());
}
@@ -116,19 +116,19 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
Class> declaringClass = descriptor.getDeclaringClass();
AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes();
if (logger.isTraceEnabled()) {
- logger.trace(String.format("Retrieved @TestExecutionListeners attributes [%s] for declaring class [%s].",
- annAttrs, declaringClass.getName()));
+ logger.trace(String.format(
+ "Retrieved @TestExecutionListeners attributes [%s] for declaring class [%s].", annAttrs,
+ declaringClass.getName()));
}
- Class extends TestExecutionListener>[] valueListenerClasses =
- (Class extends TestExecutionListener>[]) annAttrs.getClassArray("value");
- Class extends TestExecutionListener>[] listenerClasses =
- (Class extends TestExecutionListener>[]) annAttrs.getClassArray("listeners");
+ Class extends TestExecutionListener>[] valueListenerClasses = (Class extends TestExecutionListener>[]) annAttrs.getClassArray("value");
+ Class extends TestExecutionListener>[] listenerClasses = (Class extends TestExecutionListener>[]) annAttrs.getClassArray("listeners");
if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) {
- throw new IllegalStateException(String.format("Class [%s] configured with @TestExecutionListeners' " +
- "'value' [%s] and 'listeners' [%s] attributes. Use one or the other, but not both.",
- declaringClass.getName(), ObjectUtils.nullSafeToString(valueListenerClasses),
- ObjectUtils.nullSafeToString(listenerClasses)));
+ throw new IllegalStateException(String.format(
+ "Class [%s] configured with @TestExecutionListeners' "
+ + "'value' [%s] and 'listeners' [%s] attributes. Use one or the other, but not both.",
+ declaringClass.getName(), ObjectUtils.nullSafeToString(valueListenerClasses),
+ ObjectUtils.nullSafeToString(listenerClasses)));
}
else if (!ObjectUtils.isEmpty(valueListenerClasses)) {
listenerClasses = valueListenerClasses;
@@ -138,7 +138,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
classesList.addAll(0, Arrays.> asList(listenerClasses));
}
descriptor = (annAttrs.getBoolean("inheritListeners") ? MetaAnnotationUtils.findAnnotationDescriptor(
- descriptor.getRootDeclaringClass().getSuperclass(), annotationType) : null);
+ descriptor.getRootDeclaringClass().getSuperclass(), annotationType) : null);
}
}
@@ -158,10 +158,10 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
}
if (ncdfe != null) {
if (logger.isInfoEnabled()) {
- logger.info(String.format("Could not instantiate TestExecutionListener [%s]. " +
- "Specify custom listener classes or make the default listener classes " +
- "(and their required dependencies) available. Offending class: [%s]",
- listenerClass.getName(), ncdfe.getMessage()));
+ logger.info(String.format("Could not instantiate TestExecutionListener [%s]. "
+ + "Specify custom listener classes or make the default listener classes "
+ + "(and their required dependencies) available. Offending class: [%s]",
+ listenerClass.getName(), ncdfe.getMessage()));
}
}
}
@@ -186,8 +186,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
- logger.debug("Could not load default TestExecutionListener class [" + className +
- "]. Specify custom listener classes or make the default listener classes available.", ex);
+ logger.debug("Could not load default TestExecutionListener class [" + className
+ + "]. Specify custom listener classes or make the default listener classes available.", ex);
}
}
}
@@ -206,15 +206,15 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
if (MetaAnnotationUtils.findAnnotationDescriptorForTypes(testClass, ContextConfiguration.class,
ContextHierarchy.class) == null) {
if (logger.isInfoEnabled()) {
- logger.info(String.format("Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]",
- testClass.getName()));
+ logger.info(String.format(
+ "Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]",
+ testClass.getName()));
}
return new MergedContextConfiguration(testClass, null, null, null, null);
}
if (AnnotationUtils.findAnnotation(testClass, ContextHierarchy.class) != null) {
- Map> hierarchyMap =
- ContextLoaderUtils.buildContextHierarchyMap(testClass);
+ Map> hierarchyMap = ContextLoaderUtils.buildContextHierarchyMap(testClass);
MergedContextConfiguration parentConfig = null;
MergedContextConfiguration mergedConfig = null;
@@ -228,8 +228,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
Assert.notEmpty(reversedList, "ContextConfigurationAttributes list must not be empty");
Class> declaringClass = reversedList.get(0).getDeclaringClass();
- mergedConfig = buildMergedContextConfiguration(
- declaringClass, reversedList, parentConfig, cacheAwareContextLoaderDelegate);
+ mergedConfig = buildMergedContextConfiguration(declaringClass, reversedList, parentConfig,
+ cacheAwareContextLoaderDelegate);
parentConfig = mergedConfig;
}
@@ -238,8 +238,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
}
else {
return buildMergedContextConfiguration(testClass,
- ContextLoaderUtils.resolveContextConfigurationAttributes(testClass), null,
- cacheAwareContextLoaderDelegate);
+ ContextLoaderUtils.resolveContextConfigurationAttributes(testClass), null,
+ cacheAwareContextLoaderDelegate);
}
}
@@ -276,7 +276,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Processing locations and classes for context configuration attributes %s",
- configAttributes));
+ configAttributes));
}
if (contextLoader instanceof SmartContextLoader) {
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
@@ -286,7 +286,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
}
else {
String[] processedLocations = contextLoader.processLocations(configAttributes.getDeclaringClass(),
- configAttributes.getLocations());
+ configAttributes.getLocations());
locationsList.addAll(0, Arrays.asList(processedLocations));
// Legacy ContextLoaders don't know how to process classes
}
@@ -300,9 +300,11 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
Set>> initializerClasses = //
ApplicationContextInitializerUtils.resolveInitializerClasses(configAttributesList);
String[] activeProfiles = ActiveProfilesUtils.resolveActiveProfiles(testClass);
+ MergedTestPropertySources mergedTestPropertySources = TestPropertySourceUtils.buildMergedTestPropertySources(testClass);
return buildMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
- contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
+ mergedTestPropertySources.getLocations(), mergedTestPropertySources.getProperties(), contextLoader,
+ cacheAwareContextLoaderDelegate, parentConfig);
}
/**
@@ -334,7 +336,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
}
if (logger.isTraceEnabled()) {
logger.trace(String.format("Using ContextLoader class [%s] for test class [%s]",
- contextLoaderClass.getName(), testClass.getName()));
+ contextLoaderClass.getName(), testClass.getName()));
}
return BeanUtils.instantiateClass(contextLoaderClass, ContextLoader.class);
}
@@ -413,6 +415,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
* @param classes the merged annotated classes
* @param initializerClasses the merged context initializer classes
* @param activeProfiles the merged active bean definition profiles
+ * @param propertySourceLocations the merged {@code PropertySource} locations
+ * @param propertySourceProperties the merged {@code PropertySource} properties
* @param contextLoader the resolved {@code ContextLoader}
* @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate
* to be provided to the instantiated {@code MergedContextConfiguration}
@@ -421,9 +425,12 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
* @return the fully initialized {@code MergedContextConfiguration}
*/
protected abstract MergedContextConfiguration buildMergedContextConfiguration(
- Class> testClass, String[] locations, Class>[] classes,
+ Class> testClass,
+ String[] locations,
+ Class>[] classes,
Set>> initializerClasses,
- String[] activeProfiles, ContextLoader contextLoader,
- CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parentConfig);
+ String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
+ ContextLoader contextLoader, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
+ MergedContextConfiguration parentConfig);
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContextBootstrapper.java
index 19d89b98717..4c3f8f9861b 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContextBootstrapper.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContextBootstrapper.java
@@ -81,16 +81,19 @@ public class DefaultTestContextBootstrapper extends AbstractTestContextBootstrap
/**
* Builds a standard {@link MergedContextConfiguration}.
*/
+ @Override
protected MergedContextConfiguration buildMergedContextConfiguration(
Class> testClass,
String[] locations,
Class>[] classes,
Set>> initializerClasses,
- String[] activeProfiles, ContextLoader contextLoader,
- CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parentConfig) {
+ String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
+ ContextLoader contextLoader, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
+ MergedContextConfiguration parentConfig) {
return new MergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
- contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
+ propertySourceLocations, propertySourceProperties, contextLoader, cacheAwareContextLoaderDelegate,
+ parentConfig);
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/MergedTestPropertySources.java b/spring-test/src/main/java/org/springframework/test/context/support/MergedTestPropertySources.java
new file mode 100644
index 00000000000..a50933a719b
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/support/MergedTestPropertySources.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.support;
+
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.util.Assert;
+
+/**
+ * {@code MergedTestPropertySources} encapsulates the merged
+ * property sources declared on a test class and all of its superclasses
+ * via {@link TestPropertySource @TestPropertySource}.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ * @see TestPropertySource
+ */
+class MergedTestPropertySources {
+
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ private final String[] locations;
+
+ private final String[] properties;
+
+
+ /**
+ * Create an empty {@code MergedTestPropertySources} instance.
+ */
+ MergedTestPropertySources() {
+ this(EMPTY_STRING_ARRAY, EMPTY_STRING_ARRAY);
+ }
+
+ /**
+ * Create a {@code MergedTestPropertySources} instance with the supplied
+ * {@code locations} and {@code properties}.
+ * @param locations the resource locations of properties files; may be
+ * empty but never {@code null}
+ * @param properties the properties in the form of {@code key=value} pairs;
+ * may be empty but never {@code null}
+ */
+ MergedTestPropertySources(String[] locations, String[] properties) {
+ Assert.notNull(locations, "The locations array must not be null");
+ Assert.notNull(properties, "The properties array must not be null");
+ this.locations = locations;
+ this.properties = properties;
+ }
+
+ /**
+ * Get the resource locations of properties files.
+ * @see TestPropertySource#locations()
+ */
+ String[] getLocations() {
+ return this.locations;
+ }
+
+ /**
+ * Get the properties in the form of key-value pairs.
+ * @see TestPropertySource#properties()
+ */
+ String[] getProperties() {
+ return this.properties;
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceAttributes.java b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceAttributes.java
new file mode 100644
index 00000000000..c253af169c2
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceAttributes.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.support;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.style.ToStringCreator;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.ResourceUtils;
+
+/**
+ * {@code TestPropertySourceAttributes} encapsulates the attributes declared
+ * via {@link TestPropertySource @TestPropertySource}.
+ *
+ * In addition to encapsulating declared attributes,
+ * {@code TestPropertySourceAttributes} also enforces configuration rules
+ * and detects default properties files.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ * @see TestPropertySource
+ * @see MergedTestPropertySources
+ */
+class TestPropertySourceAttributes {
+
+ private static final Log logger = LogFactory.getLog(TestPropertySourceAttributes.class);
+
+ private final Class> declaringClass;
+
+ private final String[] locations;
+
+ private final boolean inheritLocations;
+
+ private final String[] properties;
+
+ private final boolean inheritProperties;
+
+
+ /**
+ * Create a new {@code TestPropertySourceAttributes} instance for the
+ * supplied {@link AnnotationAttributes} (parsed from a
+ * {@link TestPropertySource @TestPropertySource} annotation) and
+ * the {@linkplain Class test class} that declared them, enforcing
+ * configuration rules and detecting a default properties file if
+ * necessary.
+ * @param declaringClass the class that declared {@code @TestPropertySource}
+ * @param annAttrs the annotation attributes from which to retrieve the attributes
+ */
+ TestPropertySourceAttributes(Class> declaringClass, AnnotationAttributes annAttrs) {
+ this(declaringClass, resolveLocations(declaringClass, annAttrs.getStringArray("locations"),
+ annAttrs.getStringArray("value")), annAttrs.getBoolean("inheritLocations"),
+ annAttrs.getStringArray("properties"), annAttrs.getBoolean("inheritProperties"));
+ }
+
+ private TestPropertySourceAttributes(Class> declaringClass, String[] locations, boolean inheritLocations,
+ String[] properties, boolean inheritProperties) {
+ Assert.notNull(declaringClass, "declaringClass must not be null");
+
+ if (ObjectUtils.isEmpty(locations) && ObjectUtils.isEmpty(properties)) {
+ locations = new String[] { detectDefaultPropertiesFile(declaringClass) };
+ }
+
+ this.declaringClass = declaringClass;
+ this.locations = locations;
+ this.inheritLocations = inheritLocations;
+ this.properties = properties;
+ this.inheritProperties = inheritProperties;
+ }
+
+ /**
+ * Get the {@linkplain Class class} that declared {@code @TestPropertySource}.
+ *
+ * @return the declaring class; never {@code null}
+ */
+ Class> getDeclaringClass() {
+ return declaringClass;
+ }
+
+ /**
+ * Get the resource locations that were declared via {@code @TestPropertySource}.
+ *
+ *
Note: The returned value may represent a detected default
+ * that does not match the original value declared via {@code @TestPropertySource}.
+ *
+ * @return the resource locations; potentially {@code null} or empty
+ * @see TestPropertySource#value
+ * @see TestPropertySource#locations
+ * @see #setLocations(String[])
+ */
+ String[] getLocations() {
+ return locations;
+ }
+
+ /**
+ * Get the {@code inheritLocations} flag that was declared via {@code @TestPropertySource}.
+ *
+ * @return the {@code inheritLocations} flag
+ * @see TestPropertySource#inheritLocations
+ */
+ boolean isInheritLocations() {
+ return inheritLocations;
+ }
+
+ /**
+ * Get the inlined properties that were declared via {@code @TestPropertySource}.
+ *
+ * @return the inlined properties; potentially {@code null} or empty
+ * @see TestPropertySource#properties
+ */
+ String[] getProperties() {
+ return this.properties;
+ }
+
+ /**
+ * Get the {@code inheritProperties} flag that was declared via {@code @TestPropertySource}.
+ *
+ * @return the {@code inheritProperties} flag
+ * @see TestPropertySource#inheritProperties
+ */
+ boolean isInheritProperties() {
+ return this.inheritProperties;
+ }
+
+ /**
+ * Provide a String representation of the {@code @TestPropertySource}
+ * attributes and declaring class.
+ */
+ @Override
+ public String toString() {
+ return new ToStringCreator(this)//
+ .append("declaringClass", declaringClass.getName())//
+ .append("locations", ObjectUtils.nullSafeToString(locations))//
+ .append("inheritLocations", inheritLocations)//
+ .append("properties", ObjectUtils.nullSafeToString(properties))//
+ .append("inheritProperties", inheritProperties)//
+ .toString();
+ }
+
+ /**
+ * Resolve resource locations from the supplied {@code locations} and
+ * {@code value} arrays, which correspond to attributes of the same names in
+ * the {@link TestPropertySource} annotation.
+ *
+ * @throws IllegalStateException if both the locations and value attributes have been declared
+ */
+ private static String[] resolveLocations(Class> declaringClass, String[] locations, String[] value) {
+ Assert.notNull(declaringClass, "declaringClass must not be null");
+
+ if (!ObjectUtils.isEmpty(value) && !ObjectUtils.isEmpty(locations)) {
+ String msg = String.format("Class [%s] has been configured with @TestPropertySource's 'value' [%s] "
+ + "and 'locations' [%s] attributes. Only one declaration of resource "
+ + "locations is permitted per @TestPropertySource annotation.", declaringClass.getName(),
+ ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(locations));
+ logger.error(msg);
+ throw new IllegalStateException(msg);
+ }
+ else if (!ObjectUtils.isEmpty(value)) {
+ locations = value;
+ }
+
+ return locations;
+ }
+
+ /**
+ * Detect a default properties file for the supplied class, as specified
+ * in the class-level Javadoc for {@link TestPropertySource}.
+ */
+ private static String detectDefaultPropertiesFile(Class> testClass) {
+ String resourcePath = ClassUtils.convertClassNameToResourcePath(testClass.getName()) + ".properties";
+ String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath;
+ ClassPathResource classPathResource = new ClassPathResource(resourcePath);
+
+ if (classPathResource.exists()) {
+ if (logger.isInfoEnabled()) {
+ logger.info(String.format("Detected default properties file \"%s\" for test class [%s]",
+ prefixedResourcePath, testClass.getName()));
+ }
+ return prefixedResourcePath;
+ }
+ else {
+ String msg = String.format("Could not detect default properties file for test [%s]: "
+ + "%s does not exist. Either declare the 'locations' or 'properties' attributes "
+ + "of @TestPropertySource or make the default properties file available.", testClass.getName(),
+ classPathResource);
+ logger.error(msg);
+ throw new IllegalStateException(msg);
+ }
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java
new file mode 100644
index 00000000000..8bc0360e5c7
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.support;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.util.TestContextResourceUtils;
+import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+import static org.springframework.test.util.MetaAnnotationUtils.*;
+
+/**
+ * Utility methods for working with {@link TestPropertySource @TestPropertySource}.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ * @see TestPropertySource
+ */
+abstract class TestPropertySourceUtils {
+
+ private static final Log logger = LogFactory.getLog(TestPropertySourceUtils.class);
+
+
+ private TestPropertySourceUtils() {
+ /* no-op */
+ }
+
+ static MergedTestPropertySources buildMergedTestPropertySources(Class> testClass) {
+ Class annotationType = TestPropertySource.class;
+ AnnotationDescriptor descriptor = findAnnotationDescriptor(testClass, annotationType);
+ if (descriptor == null) {
+ return new MergedTestPropertySources();
+ }
+
+ // else...
+ List attributesList = resolveTestPropertySourceAttributes(testClass);
+ String[] locations = mergeLocations(attributesList);
+ String[] properties = mergeProperties(attributesList);
+ return new MergedTestPropertySources(locations, properties);
+ }
+
+ private static List resolveTestPropertySourceAttributes(Class> testClass) {
+ Assert.notNull(testClass, "Class must not be null");
+
+ final List attributesList = new ArrayList();
+ final Class annotationType = TestPropertySource.class;
+ AnnotationDescriptor descriptor = findAnnotationDescriptor(testClass, annotationType);
+ Assert.notNull(descriptor, String.format(
+ "Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]",
+ annotationType.getName(), testClass.getName()));
+
+ while (descriptor != null) {
+ AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes();
+ Class> rootDeclaringClass = descriptor.getRootDeclaringClass();
+
+ if (logger.isTraceEnabled()) {
+ logger.trace(String.format("Retrieved @TestPropertySource attributes [%s] for declaring class [%s].",
+ annAttrs, rootDeclaringClass.getName()));
+ }
+
+ TestPropertySourceAttributes attributes = new TestPropertySourceAttributes(rootDeclaringClass, annAttrs);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Resolved TestPropertySource attributes: " + attributes);
+ }
+ attributesList.add(attributes);
+
+ descriptor = findAnnotationDescriptor(rootDeclaringClass.getSuperclass(), annotationType);
+ }
+
+ return attributesList;
+ }
+
+ private static String[] mergeLocations(List attributesList) {
+ final List locations = new ArrayList();
+
+ for (TestPropertySourceAttributes attrs : attributesList) {
+ if (logger.isTraceEnabled()) {
+ logger.trace(String.format("Processing locations for TestPropertySource attributes %s", attrs));
+ }
+
+ String[] locationsArray = TestContextResourceUtils.convertToClasspathResourcePaths(
+ attrs.getDeclaringClass(), attrs.getLocations());
+ locations.addAll(0, Arrays. asList(locationsArray));
+
+ if (!attrs.isInheritLocations()) {
+ break;
+ }
+ }
+
+ return StringUtils.toStringArray(locations);
+ }
+
+ private static String[] mergeProperties(List attributesList) {
+ final List properties = new ArrayList();
+
+ for (TestPropertySourceAttributes attrs : attributesList) {
+ if (logger.isTraceEnabled()) {
+ logger.trace(String.format("Processing inlined properties for TestPropertySource attributes %s", attrs));
+ }
+
+ properties.addAll(0, Arrays. asList(attrs.getProperties()));
+
+ if (!attrs.isInheritProperties()) {
+ break;
+ }
+ }
+
+ return StringUtils.toStringArray(properties);
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java
index 58e8b38607a..2a2aa5765e3 100644
--- a/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java
+++ b/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,16 +60,9 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
/**
* Create a new {@code WebMergedContextConfiguration} instance for the
- * supplied test class, resource locations, annotated classes, context
- * initializers, active profiles, resource base path, and {@code ContextLoader}.
- *
- * If a {@code null} value is supplied for {@code locations},
- * {@code classes}, or {@code activeProfiles} an empty array will
- * be stored instead. If a {@code null} value is supplied for the
- * {@code contextInitializerClasses} an empty set will be stored instead.
- * If an empty value is supplied for the {@code resourceBasePath}
- * an empty string will be used. Furthermore, active profiles will be sorted,
- * and duplicate profiles will be removed.
+ * supplied parameters.
+ *
Delegates to
+ * {@link #WebMergedContextConfiguration(Class, String[], Class[], Set, String[], String[], String[], String, ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}.
*
* @param testClass the test class for which the configuration was merged
* @param locations the merged resource locations
@@ -90,18 +83,52 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
Set>> contextInitializerClasses,
String[] activeProfiles, String resourceBasePath, ContextLoader contextLoader) {
- this(testClass, locations, classes, contextInitializerClasses, activeProfiles, resourceBasePath, contextLoader,
+ this(testClass, locations, classes, contextInitializerClasses, activeProfiles, null, null, resourceBasePath,
+ contextLoader,
null, null);
}
/**
* Create a new {@code WebMergedContextConfiguration} instance for the
- * supplied test class, resource locations, annotated classes, context
- * initializers, active profiles, resource base path, and {@code ContextLoader}.
+ * supplied parameters.
+ * Delegates to
+ * {@link #WebMergedContextConfiguration(Class, String[], Class[], Set, String[], String[], String[], String, ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}.
+ *
+ * @param testClass the test class for which the configuration was merged
+ * @param locations the merged resource locations
+ * @param classes the merged annotated classes
+ * @param contextInitializerClasses the merged context initializer classes
+ * @param activeProfiles the merged active bean definition profiles
+ * @param resourceBasePath the resource path to the root directory of the web application
+ * @param contextLoader the resolved {@code ContextLoader}
+ * @param cacheAwareContextLoaderDelegate a cache-aware context loader
+ * delegate with which to retrieve the parent context
+ * @param parent the parent configuration or {@code null} if there is no parent
+ * @since 3.2.2
+ * @deprecated as of Spring 4.1, use
+ * {@link #WebMergedContextConfiguration(Class, String[], Class[], Set, String[], String[], String[], String, ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}
+ * instead.
+ */
+ public WebMergedContextConfiguration(
+ Class> testClass,
+ String[] locations,
+ Class>[] classes,
+ Set>> contextInitializerClasses,
+ String[] activeProfiles, String resourceBasePath, ContextLoader contextLoader,
+ CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
+
+ this(testClass, locations, classes, contextInitializerClasses, activeProfiles, null, null, resourceBasePath,
+ contextLoader, cacheAwareContextLoaderDelegate, parent);
+ }
+
+ /**
+ * Create a new {@code WebMergedContextConfiguration} instance for the
+ * supplied parameters.
*
* If a {@code null} value is supplied for {@code locations},
- * {@code classes}, or {@code activeProfiles} an empty array will
- * be stored instead. If a {@code null} value is supplied for the
+ * {@code classes}, {@code activeProfiles}, {@code propertySourceLocations},
+ * or {@code propertySourceProperties} an empty array will be stored instead.
+ * If a {@code null} value is supplied for the
* {@code contextInitializerClasses} an empty set will be stored instead.
* If an empty value is supplied for the {@code resourceBasePath}
* an empty string will be used. Furthermore, active profiles will be sorted,
@@ -112,23 +139,26 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
* @param classes the merged annotated classes
* @param contextInitializerClasses the merged context initializer classes
* @param activeProfiles the merged active bean definition profiles
+ * @param propertySourceLocations the merged {@code PropertySource} locations
+ * @param propertySourceProperties the merged {@code PropertySource} properties
* @param resourceBasePath the resource path to the root directory of the web application
* @param contextLoader the resolved {@code ContextLoader}
* @param cacheAwareContextLoaderDelegate a cache-aware context loader
* delegate with which to retrieve the parent context
* @param parent the parent configuration or {@code null} if there is no parent
- * @since 3.2.2
+ * @since 4.1
*/
public WebMergedContextConfiguration(
Class> testClass,
String[] locations,
Class>[] classes,
Set>> contextInitializerClasses,
- String[] activeProfiles, String resourceBasePath, ContextLoader contextLoader,
+ String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
+ String resourceBasePath, ContextLoader contextLoader,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
- super(testClass, locations, classes, contextInitializerClasses, activeProfiles, contextLoader,
- cacheAwareContextLoaderDelegate, parent);
+ super(testClass, locations, classes, contextInitializerClasses, activeProfiles, propertySourceLocations,
+ propertySourceProperties, contextLoader, cacheAwareContextLoaderDelegate, parent);
this.resourceBasePath = !StringUtils.hasText(resourceBasePath) ? "" : resourceBasePath;
}
@@ -185,6 +215,8 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
* {@linkplain #getLocations() locations}, {@linkplain #getClasses() annotated classes},
* {@linkplain #getContextInitializerClasses() context initializer classes},
* {@linkplain #getActiveProfiles() active profiles},
+ * {@linkplain #getPropertySourceLocations() property source locations},
+ * {@linkplain #getPropertySourceProperties() property source properties},
* {@linkplain #getResourceBasePath() resource base path}, the name of the
* {@link #getContextLoader() ContextLoader}, and the
* {@linkplain #getParent() parent configuration}.
@@ -197,6 +229,8 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
.append("classes", ObjectUtils.nullSafeToString(getClasses()))//
.append("contextInitializerClasses", ObjectUtils.nullSafeToString(getContextInitializerClasses()))//
.append("activeProfiles", ObjectUtils.nullSafeToString(getActiveProfiles()))//
+ .append("propertySourceLocations", ObjectUtils.nullSafeToString(getPropertySourceLocations()))//
+ .append("propertySourceProperties", ObjectUtils.nullSafeToString(getPropertySourceProperties()))//
.append("resourceBasePath", getResourceBasePath())//
.append("contextLoader", nullSafeToString(getContextLoader()))//
.append("parent", getParent())//
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java
index 46b0e4ad292..625739260b5 100644
--- a/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java
+++ b/spring-test/src/main/java/org/springframework/test/context/web/WebTestContextBootstrapper.java
@@ -89,20 +89,23 @@ public class WebTestContextBootstrapper extends DefaultTestContextBootstrapper {
String[] locations,
Class>[] classes,
Set>> initializerClasses,
- String[] activeProfiles, ContextLoader contextLoader,
- CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parentConfig) {
+ String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
+ ContextLoader contextLoader, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
+ MergedContextConfiguration parentConfig) {
WebAppConfiguration webAppConfiguration = AnnotationUtils.findAnnotation(testClass, WebAppConfiguration.class);
if (webAppConfiguration != null) {
String resourceBasePath = webAppConfiguration.value();
return new WebMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
- resourceBasePath, contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
+ propertySourceLocations, propertySourceProperties, resourceBasePath, contextLoader,
+ cacheAwareContextLoaderDelegate, parentConfig);
}
// else...
return super.buildMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
- contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
+ propertySourceLocations, propertySourceProperties, contextLoader, cacheAwareContextLoaderDelegate,
+ parentConfig);
}
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/ApplicationPropertyOverridePropertiesFileTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/ApplicationPropertyOverridePropertiesFileTestPropertySourceTests.java
new file mode 100644
index 00000000000..af6cce319ae
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/ApplicationPropertyOverridePropertiesFileTestPropertySourceTests.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.env;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests for {@link TestPropertySource @TestPropertySource}
+ * support with an explicitly named properties file that overrides a
+ * application-level property configured via
+ * {@link PropertySource @PropertySource} on an
+ * {@link Configuration @Configuration} class.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@TestPropertySource("ApplicationPropertyOverridePropertiesFileTestPropertySourceTests.properties")
+public class ApplicationPropertyOverridePropertiesFileTestPropertySourceTests {
+
+ @Autowired
+ protected Environment env;
+
+
+ @Test
+ public void verifyPropertiesAreAvailableInEnvironment() {
+ assertEquals("test override", env.getProperty("explicit"));
+ }
+
+
+ // -------------------------------------------------------------------
+
+ @Configuration
+ @PropertySource("classpath:/org/springframework/test/context/env/explicit.properties")
+ static class Config {
+ /* no user beans required for these tests */
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/ApplicationPropertyOverridePropertiesFileTestPropertySourceTests.properties b/spring-test/src/test/java/org/springframework/test/context/env/ApplicationPropertyOverridePropertiesFileTestPropertySourceTests.properties
new file mode 100644
index 00000000000..1fafe0c4534
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/ApplicationPropertyOverridePropertiesFileTestPropertySourceTests.properties
@@ -0,0 +1 @@
+explicit = test override
\ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/DefaultPropertiesFileDetectionTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/DefaultPropertiesFileDetectionTestPropertySourceTests.java
new file mode 100644
index 00000000000..5573a1658b6
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/DefaultPropertiesFileDetectionTestPropertySourceTests.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.env;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests that verify detection of a default properties file
+ * when {@link TestPropertySource @TestPropertySource} is empty.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@TestPropertySource
+public class DefaultPropertiesFileDetectionTestPropertySourceTests {
+
+ @Autowired
+ protected Environment env;
+
+
+ @Test
+ public void verifyPropertiesAreAvailableInEnvironment() {
+ // from DefaultPropertiesFileDetectionTestPropertySourceTests.properties
+ assertEnvironmentValue("riddle", "auto detected");
+ }
+
+ protected void assertEnvironmentValue(String key, String expected) {
+ assertEquals("Value of key [" + key + "].", expected, env.getProperty(key));
+ }
+
+
+ // -------------------------------------------------------------------
+
+ @Configuration
+ static class Config {
+ /* no user beans required for these tests */
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/DefaultPropertiesFileDetectionTestPropertySourceTests.properties b/spring-test/src/test/java/org/springframework/test/context/env/DefaultPropertiesFileDetectionTestPropertySourceTests.properties
new file mode 100644
index 00000000000..ae10beee1cb
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/DefaultPropertiesFileDetectionTestPropertySourceTests.properties
@@ -0,0 +1 @@
+riddle = auto detected
\ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/ExplicitPropertiesFileTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/ExplicitPropertiesFileTestPropertySourceTests.java
new file mode 100644
index 00000000000..9bc0f4e7a24
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/ExplicitPropertiesFileTestPropertySourceTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.env;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests for {@link TestPropertySource @TestPropertySource}
+ * support with an explicitly named properties file.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@TestPropertySource("explicit.properties")
+public class ExplicitPropertiesFileTestPropertySourceTests {
+
+ @Autowired
+ protected Environment env;
+
+
+ @Test
+ public void verifyPropertiesAreAvailableInEnvironment() {
+ String userHomeKey = "user.home";
+ assertEquals(System.getProperty(userHomeKey), env.getProperty(userHomeKey));
+ assertEquals("enigma", env.getProperty("explicit"));
+ }
+
+
+ // -------------------------------------------------------------------
+
+ @Configuration
+ static class Config {
+ /* no user beans required for these tests */
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/ExtendedDefaultPropertiesFileDetectionTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/ExtendedDefaultPropertiesFileDetectionTestPropertySourceTests.java
new file mode 100644
index 00000000000..106a1ff9375
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/ExtendedDefaultPropertiesFileDetectionTestPropertySourceTests.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.env;
+
+import org.junit.Test;
+import org.springframework.test.context.TestPropertySource;
+
+/**
+ * Integration tests that verify detection of default properties files
+ * when {@link TestPropertySource @TestPropertySource} is empty
+ * at multiple levels within a class hierarchy.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@TestPropertySource
+public class ExtendedDefaultPropertiesFileDetectionTestPropertySourceTests extends
+ DefaultPropertiesFileDetectionTestPropertySourceTests {
+
+ @Test
+ public void verifyPropertiesAreAvailableInEnvironment() {
+ super.verifyPropertiesAreAvailableInEnvironment();
+ // from ExtendedDefaultPropertiesFileDetectionTestPropertySourceTests.properties
+ assertEnvironmentValue("enigma", "auto detected");
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/ExtendedDefaultPropertiesFileDetectionTestPropertySourceTests.properties b/spring-test/src/test/java/org/springframework/test/context/env/ExtendedDefaultPropertiesFileDetectionTestPropertySourceTests.properties
new file mode 100644
index 00000000000..b3abbd1ac45
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/ExtendedDefaultPropertiesFileDetectionTestPropertySourceTests.properties
@@ -0,0 +1 @@
+enigma = auto detected
\ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/InheritedRelativePathPropertiesFileTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/InheritedRelativePathPropertiesFileTestPropertySourceTests.java
new file mode 100644
index 00000000000..83d093a3083
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/InheritedRelativePathPropertiesFileTestPropertySourceTests.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.env;
+
+import org.springframework.test.context.TestPropertySource;
+
+/**
+ * Integration tests for {@link TestPropertySource @TestPropertySource}
+ * support with an inherited explicitly named properties file that is
+ * referenced using a relative path.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+public class InheritedRelativePathPropertiesFileTestPropertySourceTests extends
+ ExplicitPropertiesFileTestPropertySourceTests {
+
+ /* all tests are in superclass */
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/InlinedPropertiesTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/InlinedPropertiesTestPropertySourceTests.java
new file mode 100644
index 00000000000..fd2ec5f50a3
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/InlinedPropertiesTestPropertySourceTests.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.env;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests for {@link TestPropertySource @TestPropertySource}
+ * support with an inlined properties.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@TestPropertySource(properties = { "foo = bar", "enigma: 42" })
+public class InlinedPropertiesTestPropertySourceTests {
+
+ @Autowired
+ protected Environment env;
+
+
+ @Test
+ public void verifyPropertiesAreAvailableInEnvironment() {
+ assertEquals("bar", env.getProperty("foo"));
+ assertEquals(42, env.getProperty("enigma", Integer.class).intValue());
+ }
+
+
+ // -------------------------------------------------------------------
+
+ @Configuration
+ static class Config {
+ /* no user beans required for these tests */
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/MergedPropertiesFilesOverriddenByInlinedPropertiesTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/MergedPropertiesFilesOverriddenByInlinedPropertiesTestPropertySourceTests.java
new file mode 100644
index 00000000000..3e0f404a615
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/MergedPropertiesFilesOverriddenByInlinedPropertiesTestPropertySourceTests.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.env;
+
+import org.junit.Test;
+import org.springframework.test.context.TestPropertySource;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests that verify support for overriding properties from
+ * properties files via inlined properties configured with
+ * {@link TestPropertySource @TestPropertySource}.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@TestPropertySource(properties = { "explicit = inlined", "extended = inlined1", "extended = inlined2" })
+public class MergedPropertiesFilesOverriddenByInlinedPropertiesTestPropertySourceTests extends
+ MergedPropertiesFilesTestPropertySourceTests {
+
+ @Test
+ @Override
+ public void verifyPropertiesAreAvailableInEnvironment() {
+ assertEquals("inlined", env.getProperty("explicit"));
+ }
+
+ @Test
+ @Override
+ public void verifyExtendedPropertiesAreAvailableInEnvironment() {
+ assertEquals("inlined2", env.getProperty("extended"));
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/MergedPropertiesFilesTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/MergedPropertiesFilesTestPropertySourceTests.java
new file mode 100644
index 00000000000..1d8fa6f4beb
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/MergedPropertiesFilesTestPropertySourceTests.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.env;
+
+import org.junit.Test;
+import org.springframework.test.context.TestPropertySource;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests that verify support for contributing additional properties
+ * files to the Spring {@code Environment} via {@link TestPropertySource @TestPropertySource}.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@TestPropertySource("extended.properties")
+public class MergedPropertiesFilesTestPropertySourceTests extends
+ ExplicitPropertiesFileTestPropertySourceTests {
+
+ @Test
+ public void verifyExtendedPropertiesAreAvailableInEnvironment() {
+ assertEquals(42, env.getProperty("extended", Integer.class).intValue());
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/SystemPropertyOverridePropertiesFileTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/SystemPropertyOverridePropertiesFileTestPropertySourceTests.java
new file mode 100644
index 00000000000..41d4f05e414
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/SystemPropertyOverridePropertiesFileTestPropertySourceTests.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.env;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests for {@link TestPropertySource @TestPropertySource}
+ * support with an explicitly named properties file that overrides a
+ * system property.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@TestPropertySource("SystemPropertyOverridePropertiesFileTestPropertySourceTests.properties")
+public class SystemPropertyOverridePropertiesFileTestPropertySourceTests {
+
+ private static final String KEY = SystemPropertyOverridePropertiesFileTestPropertySourceTests.class.getSimpleName() + ".riddle";
+
+ @Autowired
+ protected Environment env;
+
+
+ @BeforeClass
+ public static void setSystemProperty() {
+ System.setProperty(KEY, "override me!");
+ }
+
+ @AfterClass
+ public static void removeSystemProperty() {
+ System.setProperty(KEY, "");
+ }
+
+ @Test
+ public void verifyPropertiesAreAvailableInEnvironment() {
+ assertEquals("enigma", env.getProperty(KEY));
+ }
+
+
+ // -------------------------------------------------------------------
+
+ @Configuration
+ static class Config {
+ /* no user beans required for these tests */
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/SystemPropertyOverridePropertiesFileTestPropertySourceTests.properties b/spring-test/src/test/java/org/springframework/test/context/env/SystemPropertyOverridePropertiesFileTestPropertySourceTests.properties
new file mode 100644
index 00000000000..09c2b72e240
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/SystemPropertyOverridePropertiesFileTestPropertySourceTests.properties
@@ -0,0 +1 @@
+SystemPropertyOverridePropertiesFileTestPropertySourceTests.riddle = enigma
\ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/explicit.properties b/spring-test/src/test/java/org/springframework/test/context/env/explicit.properties
new file mode 100644
index 00000000000..972c736192b
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/explicit.properties
@@ -0,0 +1 @@
+explicit = enigma
\ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/extended.properties b/spring-test/src/test/java/org/springframework/test/context/env/extended.properties
new file mode 100644
index 00000000000..6a378359854
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/extended.properties
@@ -0,0 +1 @@
+extended = 42
\ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/subpackage/SubpackageInheritedRelativePathPropertiesFileTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/subpackage/SubpackageInheritedRelativePathPropertiesFileTestPropertySourceTests.java
new file mode 100644
index 00000000000..f5165636f15
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/env/subpackage/SubpackageInheritedRelativePathPropertiesFileTestPropertySourceTests.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.env.subpackage;
+
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.env.ExplicitPropertiesFileTestPropertySourceTests;
+
+/**
+ * Integration tests for {@link TestPropertySource @TestPropertySource}
+ * support with an inherited explicitly named properties file that is
+ * referenced using a relative path within a parent package.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+public class SubpackageInheritedRelativePathPropertiesFileTestPropertySourceTests extends
+ ExplicitPropertiesFileTestPropertySourceTests {
+
+ /* all tests are in superclass */
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java
new file mode 100644
index 00000000000..5d5281baca2
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.support;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.springframework.test.context.TestPropertySource;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.springframework.test.context.support.TestPropertySourceUtils.*;
+
+/**
+ * Unit tests for {@link TestPropertySourceUtils}.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+public class TestPropertySourceUtilsTests {
+
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+
+ private void assertMergedTestPropertySources(Class> testClass, String[] expectedLocations,
+ String[] expectedProperties) {
+ MergedTestPropertySources mergedPropertySources = buildMergedTestPropertySources(testClass);
+ assertNotNull(mergedPropertySources);
+ assertArrayEquals(expectedLocations, mergedPropertySources.getLocations());
+ assertArrayEquals(expectedProperties, mergedPropertySources.getProperties());
+ }
+
+ @Test
+ public void emptyAnnotation() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage(startsWith("Could not detect default properties file for test"));
+ expectedException.expectMessage(containsString("EmptyPropertySources.properties"));
+ buildMergedTestPropertySources(EmptyPropertySources.class);
+ }
+
+ @Test
+ public void extendedEmptyAnnotation() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage(startsWith("Could not detect default properties file for test"));
+ expectedException.expectMessage(containsString("ExtendedEmptyPropertySources.properties"));
+ buildMergedTestPropertySources(ExtendedEmptyPropertySources.class);
+ }
+
+ @Test
+ public void value() {
+ assertMergedTestPropertySources(ValuePropertySources.class, new String[] { "classpath:/value.xml" },
+ EMPTY_STRING_ARRAY);
+ }
+
+ @Test
+ public void locationsAndValueAttributes() {
+ expectedException.expect(IllegalStateException.class);
+ buildMergedTestPropertySources(LocationsAndValuePropertySources.class);
+ }
+
+ @Test
+ public void locationsAndProperties() {
+ assertMergedTestPropertySources(LocationsAndPropertiesPropertySources.class, new String[] {
+ "classpath:/foo1.xml", "classpath:/foo2.xml" }, new String[] { "k1a=v1a", "k1b: v1b" });
+ }
+
+ @Test
+ public void inheritedLocationsAndProperties() {
+ assertMergedTestPropertySources(InheritedPropertySources.class, new String[] { "classpath:/foo1.xml",
+ "classpath:/foo2.xml" }, new String[] { "k1a=v1a", "k1b: v1b" });
+ }
+
+ @Test
+ public void extendedLocationsAndProperties() {
+ assertMergedTestPropertySources(ExtendedPropertySources.class, new String[] { "classpath:/foo1.xml",
+ "classpath:/foo2.xml", "classpath:/bar1.xml", "classpath:/bar2.xml" }, new String[] { "k1a=v1a",
+ "k1b: v1b", "k2a v2a", "k2b: v2b" });
+ }
+
+ @Test
+ public void overriddenLocations() {
+ assertMergedTestPropertySources(OverriddenLocationsPropertySources.class,
+ new String[] { "classpath:/baz.properties" }, new String[] { "k1a=v1a", "k1b: v1b", "key = value" });
+ }
+
+ @Test
+ public void overriddenProperties() {
+ assertMergedTestPropertySources(OverriddenPropertiesPropertySources.class, new String[] {
+ "classpath:/foo1.xml", "classpath:/foo2.xml", "classpath:/baz.properties" }, new String[] { "key = value" });
+ }
+
+ @Test
+ public void overriddenLocationsAndProperties() {
+ assertMergedTestPropertySources(OverriddenLocationsAndPropertiesPropertySources.class,
+ new String[] { "classpath:/baz.properties" }, new String[] { "key = value" });
+ }
+
+
+ // -------------------------------------------------------------------
+
+ @TestPropertySource
+ static class EmptyPropertySources {
+ }
+
+ @TestPropertySource
+ static class ExtendedEmptyPropertySources extends EmptyPropertySources {
+ }
+
+ @TestPropertySource(locations = "/foo", value = "/bar")
+ static class LocationsAndValuePropertySources {
+ }
+
+ @TestPropertySource("/value.xml")
+ static class ValuePropertySources {
+ }
+
+ @TestPropertySource(locations = { "/foo1.xml", "/foo2.xml" }, properties = { "k1a=v1a", "k1b: v1b" })
+ static class LocationsAndPropertiesPropertySources {
+ }
+
+ static class InheritedPropertySources extends LocationsAndPropertiesPropertySources {
+ }
+
+ @TestPropertySource(locations = { "/bar1.xml", "/bar2.xml" }, properties = { "k2a v2a", "k2b: v2b" })
+ static class ExtendedPropertySources extends LocationsAndPropertiesPropertySources {
+ }
+
+ @TestPropertySource(locations = "/baz.properties", properties = "key = value", inheritLocations = false)
+ static class OverriddenLocationsPropertySources extends LocationsAndPropertiesPropertySources {
+ }
+
+ @TestPropertySource(locations = "/baz.properties", properties = "key = value", inheritProperties = false)
+ static class OverriddenPropertiesPropertySources extends LocationsAndPropertiesPropertySources {
+ }
+
+ @TestPropertySource(locations = "/baz.properties", properties = "key = value", inheritLocations = false, inheritProperties = false)
+ static class OverriddenLocationsAndPropertiesPropertySources extends LocationsAndPropertiesPropertySources {
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/web/AnnotationConfigWebContextLoaderTests.java b/spring-test/src/test/java/org/springframework/test/context/web/AnnotationConfigWebContextLoaderTests.java
index fed649315e5..8b78fa14645 100644
--- a/spring-test/src/test/java/org/springframework/test/context/web/AnnotationConfigWebContextLoaderTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/web/AnnotationConfigWebContextLoaderTests.java
@@ -38,6 +38,7 @@ public class AnnotationConfigWebContextLoaderTests {
@Test
+ @SuppressWarnings("deprecation")
public void configMustNotContainLocations() throws Exception {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(containsString("does not support resource locations"));
diff --git a/spring-test/src/test/java/org/springframework/test/context/web/GenericXmlWebContextLoaderTests.java b/spring-test/src/test/java/org/springframework/test/context/web/GenericXmlWebContextLoaderTests.java
index d8206e29166..4357ca752ab 100644
--- a/spring-test/src/test/java/org/springframework/test/context/web/GenericXmlWebContextLoaderTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/web/GenericXmlWebContextLoaderTests.java
@@ -37,6 +37,7 @@ public class GenericXmlWebContextLoaderTests {
@Test
+ @SuppressWarnings("deprecation")
public void configMustNotContainAnnotatedClasses() throws Exception {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(containsString("does not support annotated classes"));