From 2cf4147ba81503a45922dc38444b6874f7ba6221 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 1 Aug 2014 14:33:11 +0300 Subject: [PATCH] Introduce @TestPropertySource support in the TCF Spring Framework 3.1 introduced an Environment abstraction with support for hierarchical PropertySources that can be configured programmatically as well as declaratively via the @PropertySource annotation. However, prior to this commit, there was no way to declaratively configure PropertySources in integration tests in the Spring TestContext Framework (TCF). This commit introduces declarative support for PropertySources in the TCF via a new class-level @TestPropertySource annotation. This annotation provides two options for declaring test property sources: - The 'locations' attribute allows developers to declare external resource locations for test properties files. - The 'properties' attribute allows developers to declare inlined properties in the form of key-value pairs. Test properties files are added to the Environment before all other property sources and can therefore override system and application property sources. Similarly, inlined properties are added to the Environment before all other property sources and can therefore override system property sources, application property sources, and test properties files. Specifically, this commit introduces the following major changes: - Introduced @TestPropertySource annotation along with internal TestPropertySourceAttributes, MergedTestPropertySources, and TestPropertySourceUtils for working with test property sources within the TCF. - All TestContextBootstrappers have been modified to support the merged property resource locations and inlined properties from @TestPropertySource. - MergedContextConfiguration (and consequently the context caching key) is now additionally based on the merged property resource locations and inlined properties from @TestPropertySource. The same applies to WebMergedContextConfiguration. - AbstractContextLoader's prepareContext() method now adds PropertySources for all resource locations and inlined properties from the supplied MergedContextConfiguration to the Environment of the supplied ApplicationContext. All subclasses of AbstractGenericContextLoader and AbstractGenericWebContextLoader therefore automatically provide support for @TestPropertySource. Issue: SPR-12051 --- .../test/context/ContextConfiguration.java | 14 +- .../context/MergedContextConfiguration.java | 164 ++++++++---- .../test/context/TestPropertySource.java | 250 ++++++++++++++++++ .../support/AbstractContextLoader.java | 105 +++++++- .../AbstractTestContextBootstrapper.java | 79 +++--- .../DefaultTestContextBootstrapper.java | 9 +- .../support/MergedTestPropertySources.java | 78 ++++++ .../support/TestPropertySourceAttributes.java | 209 +++++++++++++++ .../support/TestPropertySourceUtils.java | 133 ++++++++++ .../web/WebMergedContextConfiguration.java | 74 ++++-- .../web/WebTestContextBootstrapper.java | 11 +- ...PropertiesFileTestPropertySourceTests.java | 64 +++++ ...tiesFileTestPropertySourceTests.properties | 1 + ...sFileDetectionTestPropertySourceTests.java | 64 +++++ ...etectionTestPropertySourceTests.properties | 1 + ...PropertiesFileTestPropertySourceTests.java | 61 +++++ ...sFileDetectionTestPropertySourceTests.java | 41 +++ ...etectionTestPropertySourceTests.properties | 1 + ...PropertiesFileTestPropertySourceTests.java | 34 +++ ...inedPropertiesTestPropertySourceTests.java | 60 +++++ ...inedPropertiesTestPropertySourceTests.java | 48 ++++ ...ropertiesFilesTestPropertySourceTests.java | 40 +++ ...PropertiesFileTestPropertySourceTests.java | 74 ++++++ ...tiesFileTestPropertySourceTests.properties | 1 + .../test/context/env/explicit.properties | 1 + .../test/context/env/extended.properties | 1 + ...PropertiesFileTestPropertySourceTests.java | 35 +++ .../support/TestPropertySourceUtilsTests.java | 157 +++++++++++ ...AnnotationConfigWebContextLoaderTests.java | 1 + .../web/GenericXmlWebContextLoaderTests.java | 1 + 30 files changed, 1686 insertions(+), 126 deletions(-) create mode 100644 spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java create mode 100644 spring-test/src/main/java/org/springframework/test/context/support/MergedTestPropertySources.java create mode 100644 spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceAttributes.java create mode 100644 spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/ApplicationPropertyOverridePropertiesFileTestPropertySourceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/ApplicationPropertyOverridePropertiesFileTestPropertySourceTests.properties create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/DefaultPropertiesFileDetectionTestPropertySourceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/DefaultPropertiesFileDetectionTestPropertySourceTests.properties create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/ExplicitPropertiesFileTestPropertySourceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/ExtendedDefaultPropertiesFileDetectionTestPropertySourceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/ExtendedDefaultPropertiesFileDetectionTestPropertySourceTests.properties create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/InheritedRelativePathPropertiesFileTestPropertySourceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/InlinedPropertiesTestPropertySourceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/MergedPropertiesFilesOverriddenByInlinedPropertiesTestPropertySourceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/MergedPropertiesFilesTestPropertySourceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/SystemPropertyOverridePropertiesFileTestPropertySourceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/SystemPropertyOverridePropertiesFileTestPropertySourceTests.properties create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/explicit.properties create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/extended.properties create mode 100644 spring-test/src/test/java/org/springframework/test/context/env/subpackage/SubpackageInheritedRelativePathPropertiesFileTestPropertySourceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java 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. * * * *

- * 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

+ * + * + * @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}: + *

+ * + *

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 { * - *

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[] valueListenerClasses = - (Class[]) annAttrs.getClassArray("value"); - Class[] listenerClasses = - (Class[]) annAttrs.getClassArray("listeners"); + Class[] valueListenerClasses = (Class[]) annAttrs.getClassArray("value"); + Class[] listenerClasses = (Class[]) 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"));