提交 2cf4147b 编写于 作者: S Sam Brannen

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
上级 66656346
......@@ -53,22 +53,19 @@ import org.springframework.context.ConfigurableApplicationContext;
* The term <em>annotated class</em> can refer to any of the following.
*
* <ul>
* <li>A class annotated with @{@link org.springframework.context.annotation.Configuration
* Configuration}</li>
* <li>A class annotated with {@link org.springframework.context.annotation.Configuration @Configuration}</li>
* <li>A component (i.e., a class annotated with
* {@link org.springframework.stereotype.Component @Component},
* {@link org.springframework.stereotype.Service @Service},
* {@link org.springframework.stereotype.Repository @Repository}, etc.)</li>
* <li>A JSR-330 compliant class that is annotated with {@code javax.inject} annotations</li>
* <li>Any other class that contains {@link org.springframework.context.annotation.Bean
* @Bean}-methods</li>
* <li>Any other class that contains {@link org.springframework.context.annotation.Bean @Bean}-methods</li>
* </ul>
*
* <p>
* 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
* <em>annotated classes</em>.
* 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 <em>annotated classes</em>.
*
* <p>
* As of Spring Framework 4.0, this annotation may be used as a <em>meta-annotation</em>
......@@ -78,6 +75,7 @@ import org.springframework.context.ConfigurableApplicationContext;
* @since 2.5
* @see ContextHierarchy
* @see ActiveProfiles
* @see TestPropertySource
* @see ContextLoader
* @see SmartContextLoader
* @see ContextConfigurationAttributes
......
/*
* 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 <em>merged</em>
* 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}.
*
* <p>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.
* <p>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.
*
* <p>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<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> 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}.
*
* <p>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.
* <p>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}.
*
* <p>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.
* <p>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.
*
* <p>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.
* <p>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<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> 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.
*
* <p>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<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> 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}.
* <p>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}.
* <p>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();
......
/*
* 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.
*
* <h3>Precedence</h3>
* <p>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}.
*
* <h3>Default Properties File Detection</h3>
* <p>If {@code @TestPropertySource} is declared as an <em>empty</em> annotation
* (i.e., without explicit values for {@link #locations} or {@link #properties}),
* an attempt will be made to detect a <em>default</em> 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.
*
* <h3>Enabling &#064;TestPropertySource</h3>
* <p>{@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.
*
* <h3>Miscellaneous</h3>
* <ul>
* <li>Typically, {@code @TestPropertySource} will be used in conjunction with
* {@link ContextConfiguration @ContextConfiguration}.</li>
* <li>This annotation may be used as a <em>meta-annotation</em> to create
* custom <em>composed annotations</em>; however, caution should be taken if
* this annotation and {@code @ContextConfiguration} are combined on a composed
* annotation since the {@code locations} and {@code inheritLocations} attributes
* of both annotations can lead to ambiguity during the attribute resolution
* process.</li>
* </ul>
*
* @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}.
*
* <p>This attribute may <strong>not</strong> be used in conjunction with
* {@link #locations}, but it may be used <em>instead</em> 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.
*
* <h3>Supported File Formats</h3>
* <p>Both traditional and XML-based properties file formats are supported
* &mdash; for example, {@code "classpath:/com/example/test.properties"}
* or {@code "file:/path/to/file.xml"}.
*
* <h3>Path Resource Semantics</h3>
* <p>Each path will be interpreted as a Spring
* {@link org.springframework.core.io.Resource Resource}. A plain path
* &mdash; for example, {@code "test.properties"} &mdash; will be treated as a
* classpath resource that is <em>relative</em> to the package in which the
* test class is defined. A path starting with a slash will be treated as an
* <em>absolute</em> 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. <code>*&#42;/*.properties</code>)
* are not permitted: each location must evaluate to exactly one
* {@code .properties} or {@code .xml} resource.
*
* <h3>Default Properties File Detection</h3>
* <p>See the class-level Javadoc for a discussion on detection of defaults.
*
* <h3>Precedence</h3>
* <p>Properties loaded from resource locations have lower precedence than
* inlined {@link #properties}.
*
* <p>This attribute may <strong>not</strong> be used in conjunction with
* {@link #value}, but it may be used <em>instead</em> 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 <em>inherited</em>.
*
* <p>The default value is {@code true}, which means that a test class will
* <em>inherit</em> 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 <em>extending</em> the list of test
* property source locations.
*
* <p>If {@code inheritLocations} is set to {@code false}, the property
* source locations for the test class will <em>shadow</em> and effectively
* replace any property source locations defined by a superclass.
*
* <p>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"}
* <strong>and</strong> {@code "extended.properties"} files as test property
* source locations.
* <pre class="code">
* &#064;TestPropertySource(&quot;base.properties&quot;)
* &#064;ContextConfiguration
* public class BaseTest {
* // ...
* }
*
* &#064;TestPropertySource(&quot;extended.properties&quot;)
* &#064;ContextConfiguration
* public class ExtendedTest extends BaseTest {
* // ...
* }
* </pre>
*
* @see #locations
*/
boolean inheritLocations() default true;
/**
* <em>Inlined properties</em> in the form of <em>key-value</em> 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.
*
* <h3>Supported Syntax</h3>
* <p>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}:
* <ul>
* <li>{@code "key=value"}</li>
* <li>{@code "key:value"}</li>
* <li>{@code "key value"}</li>
* </ul>
*
* <h3>Precedence</h3>
* <p>Properties declared via this attribute have higher precedence than
* properties loaded from resource {@link locations}.
*
* <p>This attribute may be used in conjunction with {@link #value}
* <em>or</em> {@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 <em>inherited</em>.
*
* <p>The default value is {@code true}, which means that a test class will
* <em>inherit</em> 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 <em>extending</em> the list of inlined test properties.
*
* <p>If {@code inheritProperties} is set to {@code false}, the inlined
* properties for the test class will <em>shadow</em> and effectively
* replace any inlined properties defined by a superclass.
*
* <p>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}
* <strong>and</strong> {@code key2} properties.
* <pre class="code">
* &#064;TestPropertySource(properties = &quot;key1 = value1&quot;)
* &#064;ContextConfiguration
* public class BaseTest {
* // ...
* }
*
* &#064;TestPropertySource(properties = &quot;key2 = value2&quot;)
* &#064;ContextConfiguration
* public class ExtendedTest extends BaseTest {
* // ...
* }
* </pre>
*
* @see #properties
*/
boolean inheritProperties() default true;
}
......@@ -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 {
* <ul>
* <li>Sets the <em>active bean definition profiles</em> from the supplied
* {@code MergedContextConfiguration} in the
* {@link org.springframework.core.env.Environment Environment} of the context.</li>
* {@link org.springframework.core.env.Environment Environment} of the
* context.</li>
* <li>Adds {@link PropertySource PropertySources} for all
* {@linkplain MergedContextConfiguration#getPropertySourceLocations()
* resource locations} and
* {@linkplain MergedContextConfiguration#getPropertySourceProperties()
* inlined properties} from the supplied {@code MergedContextConfiguration}
* to the {@code Environment} of the context.</li>
* <li>Determines what (if any) context initializer classes have been supplied
* via the {@code MergedContextConfiguration} and
* {@linkplain ApplicationContextInitializer#initialize invokes each} with the
* via the {@code MergedContextConfiguration} and instantiates and
* {@linkplain ApplicationContextInitializer#initialize invokes} each with the
* given application context.</li>
* <ul>
* <li>Any {@code ApplicationContextInitializers} implementing
* {@link org.springframework.core.Ordered Ordered} or annotated with {@link
* org.springframework.core.annotation.Order @Order} will be sorted appropriately.</li>
* </ul>
* </ul>
* <p>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.
* <p>Parsing of the key/value pairs is achieved by converting all pairs
* into a single <em>virtual</em> properties file in memory and delegating
* to {@link Properties#load(java.io.Reader)} to parse that virtual file.
* <p>This code has been adapted from Spring Boot's
* {@link org.springframework.boot.test.SpringApplicationContextLoader SpringApplicationContextLoader}.
* @since 4.1
*/
private Map<String, Object> 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<String, Object> properties = new HashMap<String, Object>();
for (String name : props.stringPropertyNames()) {
properties.put(name, props.getProperty(name));
}
return properties;
}
@SuppressWarnings("unchecked")
private void invokeApplicationContextInitializers(ConfigurableApplicationContext context,
MergedContextConfiguration mergedConfig) {
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = mergedConfig.getContextInitializerClasses();
if (initializerClasses.isEmpty()) {
// no ApplicationContextInitializers have been declared -> nothing to do
......
......@@ -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<TestExecutionListeners> annotationType = TestExecutionListeners.class;
List<Class<? extends TestExecutionListener>> classesList = new ArrayList<Class<? extends TestExecutionListener>>();
AnnotationDescriptor<TestExecutionListeners> descriptor =
MetaAnnotationUtils.findAnnotationDescriptor(clazz, annotationType);
AnnotationDescriptor<TestExecutionListeners> descriptor = MetaAnnotationUtils.findAnnotationDescriptor(clazz,
annotationType);
// Use defaults?
if (descriptor == null) {
if (logger.isDebugEnabled()) {
logger.debug("@TestExecutionListeners is not present for class [" + clazz.getName() + "]: using defaults.");
logger.debug("@TestExecutionListeners is not present for class [" + clazz.getName()
+ "]: using defaults.");
}
classesList.addAll(getDefaultTestExecutionListenerClasses());
}
......@@ -116,19 +116,19 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
Class<?> declaringClass = descriptor.getDeclaringClass();
AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes();
if (logger.isTraceEnabled()) {
logger.trace(String.format("Retrieved @TestExecutionListeners attributes [%s] for declaring class [%s].",
annAttrs, declaringClass.getName()));
logger.trace(String.format(
"Retrieved @TestExecutionListeners attributes [%s] for declaring class [%s].", annAttrs,
declaringClass.getName()));
}
Class<? extends TestExecutionListener>[] valueListenerClasses =
(Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("value");
Class<? extends TestExecutionListener>[] listenerClasses =
(Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("listeners");
Class<? extends TestExecutionListener>[] valueListenerClasses = (Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("value");
Class<? extends TestExecutionListener>[] listenerClasses = (Class<? extends TestExecutionListener>[]) annAttrs.getClassArray("listeners");
if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) {
throw new IllegalStateException(String.format("Class [%s] configured with @TestExecutionListeners' " +
"'value' [%s] and 'listeners' [%s] attributes. Use one or the other, but not both.",
declaringClass.getName(), ObjectUtils.nullSafeToString(valueListenerClasses),
ObjectUtils.nullSafeToString(listenerClasses)));
throw new IllegalStateException(String.format(
"Class [%s] configured with @TestExecutionListeners' "
+ "'value' [%s] and 'listeners' [%s] attributes. Use one or the other, but not both.",
declaringClass.getName(), ObjectUtils.nullSafeToString(valueListenerClasses),
ObjectUtils.nullSafeToString(listenerClasses)));
}
else if (!ObjectUtils.isEmpty(valueListenerClasses)) {
listenerClasses = valueListenerClasses;
......@@ -138,7 +138,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> 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<String, List<ContextConfigurationAttributes>> hierarchyMap =
ContextLoaderUtils.buildContextHierarchyMap(testClass);
Map<String, List<ContextConfigurationAttributes>> 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<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> 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<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses,
String[] activeProfiles, ContextLoader contextLoader,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parentConfig);
String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
ContextLoader contextLoader, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
MergedContextConfiguration parentConfig);
}
......@@ -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<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> 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);
}
}
/*
* 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 <em>merged</em>
* 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 <em>empty</em> {@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 <em>key-value</em> pairs.
* @see TestPropertySource#properties()
*/
String[] getProperties() {
return this.properties;
}
}
/*
* 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}.
*
* <p>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}.
*
* <p>Note: The returned value may represent a <em>detected default</em>
* that does not match the original value declared via {@code @TestPropertySource}.
*
* @return the resource locations; potentially {@code null} or <em>empty</em>
* @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 <em>empty</em>
* @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);
}
}
}
/*
* 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<TestPropertySource> annotationType = TestPropertySource.class;
AnnotationDescriptor<TestPropertySource> descriptor = findAnnotationDescriptor(testClass, annotationType);
if (descriptor == null) {
return new MergedTestPropertySources();
}
// else...
List<TestPropertySourceAttributes> attributesList = resolveTestPropertySourceAttributes(testClass);
String[] locations = mergeLocations(attributesList);
String[] properties = mergeProperties(attributesList);
return new MergedTestPropertySources(locations, properties);
}
private static List<TestPropertySourceAttributes> resolveTestPropertySourceAttributes(Class<?> testClass) {
Assert.notNull(testClass, "Class must not be null");
final List<TestPropertySourceAttributes> attributesList = new ArrayList<TestPropertySourceAttributes>();
final Class<TestPropertySource> annotationType = TestPropertySource.class;
AnnotationDescriptor<TestPropertySource> 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<TestPropertySourceAttributes> attributesList) {
final List<String> locations = new ArrayList<String>();
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.<String> asList(locationsArray));
if (!attrs.isInheritLocations()) {
break;
}
}
return StringUtils.toStringArray(locations);
}
private static String[] mergeProperties(List<TestPropertySourceAttributes> attributesList) {
final List<String> properties = new ArrayList<String>();
for (TestPropertySourceAttributes attrs : attributesList) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Processing inlined properties for TestPropertySource attributes %s", attrs));
}
properties.addAll(0, Arrays.<String> asList(attrs.getProperties()));
if (!attrs.isInheritProperties()) {
break;
}
}
return StringUtils.toStringArray(properties);
}
}
/*
* 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}.
*
* <p>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 <em>empty</em> 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.
* <p>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<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> 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.
* <p>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<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> 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.
*
* <p>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 <em>empty</em> 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<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> 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())//
......
......@@ -89,20 +89,23 @@ public class WebTestContextBootstrapper extends DefaultTestContextBootstrapper {
String[] locations,
Class<?>[] classes,
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> 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);
}
}
/*
* 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 */
}
}
/*
* 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 <em>empty</em>.
*
* @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 */
}
}
/*
* 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 */
}
}
/*
* 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 <em>empty</em>
* 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");
}
}
/*
* 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 */
}
/*
* 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 */
}
}
/*
* 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"));
}
}
/*
* 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());
}
}
/*
* 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 */
}
}
SystemPropertyOverridePropertiesFileTestPropertySourceTests.riddle = enigma
\ No newline at end of file
/*
* 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 */
}
/*
* 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 {
}
}
......@@ -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"));
......
......@@ -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"));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册