Merge from sbrannen/SPR-12051

* SPR-12051:
  Introduce @TestPropertySource support in the TCF
This commit is contained in:
Sam Brannen
2014-08-14 00:05:03 +02:00
30 changed files with 1690 additions and 130 deletions

View File

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

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,15 +34,17 @@ import org.springframework.util.StringUtils;
/**
* {@code MergedContextConfiguration} encapsulates the <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();

View File

@@ -0,0 +1,250 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* {@code @TestPropertySource} is a class-level annotation that is used to
* configure the {@link #locations} of properties files and inlined
* {@link #properties} to be added to the {@code Environment}'s set of
* {@code PropertySources} for an
* {@link org.springframework.context.ApplicationContext ApplicationContext}
* for integration tests.
*
* <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;
}

View File

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

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.support;
import org.springframework.test.context.TestPropertySource;
import org.springframework.util.Assert;
/**
* {@code MergedTestPropertySources} encapsulates the <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;
}
}

View File

@@ -0,0 +1,209 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.style.ToStringCreator;
import org.springframework.test.context.TestPropertySource;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ResourceUtils;
/**
* {@code TestPropertySourceAttributes} encapsulates the attributes declared
* via {@link TestPropertySource @TestPropertySource}.
*
* <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);
}
}
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.support;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.util.TestContextResourceUtils;
import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import static org.springframework.test.util.MetaAnnotationUtils.*;
/**
* Utility methods for working with {@link TestPropertySource @TestPropertySource}.
*
* @author Sam Brannen
* @since 4.1
* @see TestPropertySource
*/
abstract class TestPropertySourceUtils {
private static final Log logger = LogFactory.getLog(TestPropertySourceUtils.class);
private TestPropertySourceUtils() {
/* no-op */
}
static MergedTestPropertySources buildMergedTestPropertySources(Class<?> testClass) {
Class<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);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,16 +60,9 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
/**
* Create a new {@code WebMergedContextConfiguration} instance for the
* supplied test class, resource locations, annotated classes, context
* initializers, active profiles, resource base path, and {@code ContextLoader}.
*
* <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,22 +83,16 @@ 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}.
*
* <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
@@ -118,6 +105,9 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
* 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,
@@ -127,8 +117,48 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
String[] activeProfiles, String resourceBasePath, ContextLoader contextLoader,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
super(testClass, locations, classes, contextInitializerClasses, activeProfiles, contextLoader,
cacheAwareContextLoaderDelegate, 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}, {@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,
* and duplicate profiles will be removed.
*
* @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 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 4.1
*/
public WebMergedContextConfiguration(
Class<?> testClass,
String[] locations,
Class<?>[] classes,
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
String resourceBasePath, ContextLoader contextLoader,
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration 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())//

View File

@@ -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);
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.env;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
/**
* Integration tests for {@link TestPropertySource @TestPropertySource}
* support with an explicitly named properties file that overrides a
* application-level property configured via
* {@link PropertySource @PropertySource} on an
* {@link Configuration @Configuration} class.
*
* @author Sam Brannen
* @since 4.1
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestPropertySource("ApplicationPropertyOverridePropertiesFileTestPropertySourceTests.properties")
public class ApplicationPropertyOverridePropertiesFileTestPropertySourceTests {
@Autowired
protected Environment env;
@Test
public void verifyPropertiesAreAvailableInEnvironment() {
assertEquals("test override", env.getProperty("explicit"));
}
// -------------------------------------------------------------------
@Configuration
@PropertySource("classpath:/org/springframework/test/context/env/explicit.properties")
static class Config {
/* no user beans required for these tests */
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.env;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
/**
* Integration tests that verify detection of a default properties file
* when {@link TestPropertySource @TestPropertySource} is <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 */
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.env;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
/**
* Integration tests for {@link TestPropertySource @TestPropertySource}
* support with an explicitly named properties file.
*
* @author Sam Brannen
* @since 4.1
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestPropertySource("explicit.properties")
public class ExplicitPropertiesFileTestPropertySourceTests {
@Autowired
protected Environment env;
@Test
public void verifyPropertiesAreAvailableInEnvironment() {
String userHomeKey = "user.home";
assertEquals(System.getProperty(userHomeKey), env.getProperty(userHomeKey));
assertEquals("enigma", env.getProperty("explicit"));
}
// -------------------------------------------------------------------
@Configuration
static class Config {
/* no user beans required for these tests */
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.env;
import org.junit.Test;
import org.springframework.test.context.TestPropertySource;
/**
* Integration tests that verify detection of default properties files
* when {@link TestPropertySource @TestPropertySource} is <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");
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.env;
import org.springframework.test.context.TestPropertySource;
/**
* Integration tests for {@link TestPropertySource @TestPropertySource}
* support with an inherited explicitly named properties file that is
* referenced using a relative path.
*
* @author Sam Brannen
* @since 4.1
*/
public class InheritedRelativePathPropertiesFileTestPropertySourceTests extends
ExplicitPropertiesFileTestPropertySourceTests {
/* all tests are in superclass */
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.env;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
/**
* Integration tests for {@link TestPropertySource @TestPropertySource}
* support with an inlined properties.
*
* @author Sam Brannen
* @since 4.1
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestPropertySource(properties = { "foo = bar", "enigma: 42" })
public class InlinedPropertiesTestPropertySourceTests {
@Autowired
protected Environment env;
@Test
public void verifyPropertiesAreAvailableInEnvironment() {
assertEquals("bar", env.getProperty("foo"));
assertEquals(42, env.getProperty("enigma", Integer.class).intValue());
}
// -------------------------------------------------------------------
@Configuration
static class Config {
/* no user beans required for these tests */
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.env;
import org.junit.Test;
import org.springframework.test.context.TestPropertySource;
import static org.junit.Assert.*;
/**
* Integration tests that verify support for overriding properties from
* properties files via inlined properties configured with
* {@link TestPropertySource @TestPropertySource}.
*
* @author Sam Brannen
* @since 4.1
*/
@TestPropertySource(properties = { "explicit = inlined", "extended = inlined1", "extended = inlined2" })
public class MergedPropertiesFilesOverriddenByInlinedPropertiesTestPropertySourceTests extends
MergedPropertiesFilesTestPropertySourceTests {
@Test
@Override
public void verifyPropertiesAreAvailableInEnvironment() {
assertEquals("inlined", env.getProperty("explicit"));
}
@Test
@Override
public void verifyExtendedPropertiesAreAvailableInEnvironment() {
assertEquals("inlined2", env.getProperty("extended"));
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.env;
import org.junit.Test;
import org.springframework.test.context.TestPropertySource;
import static org.junit.Assert.*;
/**
* Integration tests that verify support for contributing additional properties
* files to the Spring {@code Environment} via {@link TestPropertySource @TestPropertySource}.
*
* @author Sam Brannen
* @since 4.1
*/
@TestPropertySource("extended.properties")
public class MergedPropertiesFilesTestPropertySourceTests extends
ExplicitPropertiesFileTestPropertySourceTests {
@Test
public void verifyExtendedPropertiesAreAvailableInEnvironment() {
assertEquals(42, env.getProperty("extended", Integer.class).intValue());
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.env;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
/**
* Integration tests for {@link TestPropertySource @TestPropertySource}
* support with an explicitly named properties file that overrides a
* system property.
*
* @author Sam Brannen
* @since 4.1
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestPropertySource("SystemPropertyOverridePropertiesFileTestPropertySourceTests.properties")
public class SystemPropertyOverridePropertiesFileTestPropertySourceTests {
private static final String KEY = SystemPropertyOverridePropertiesFileTestPropertySourceTests.class.getSimpleName() + ".riddle";
@Autowired
protected Environment env;
@BeforeClass
public static void setSystemProperty() {
System.setProperty(KEY, "override me!");
}
@AfterClass
public static void removeSystemProperty() {
System.setProperty(KEY, "");
}
@Test
public void verifyPropertiesAreAvailableInEnvironment() {
assertEquals("enigma", env.getProperty(KEY));
}
// -------------------------------------------------------------------
@Configuration
static class Config {
/* no user beans required for these tests */
}
}

View File

@@ -0,0 +1 @@
SystemPropertyOverridePropertiesFileTestPropertySourceTests.riddle = enigma

View File

@@ -0,0 +1 @@
explicit = enigma

View File

@@ -0,0 +1 @@
extended = 42

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.env.subpackage;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.env.ExplicitPropertiesFileTestPropertySourceTests;
/**
* Integration tests for {@link TestPropertySource @TestPropertySource}
* support with an inherited explicitly named properties file that is
* referenced using a relative path within a parent package.
*
* @author Sam Brannen
* @since 4.1
*/
public class SubpackageInheritedRelativePathPropertiesFileTestPropertySourceTests extends
ExplicitPropertiesFileTestPropertySourceTests {
/* all tests are in superclass */
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.support;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.test.context.TestPropertySource;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.springframework.test.context.support.TestPropertySourceUtils.*;
/**
* Unit tests for {@link TestPropertySourceUtils}.
*
* @author Sam Brannen
* @since 4.1
*/
public class TestPropertySourceUtilsTests {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
@Rule
public ExpectedException expectedException = ExpectedException.none();
private void assertMergedTestPropertySources(Class<?> testClass, String[] expectedLocations,
String[] expectedProperties) {
MergedTestPropertySources mergedPropertySources = buildMergedTestPropertySources(testClass);
assertNotNull(mergedPropertySources);
assertArrayEquals(expectedLocations, mergedPropertySources.getLocations());
assertArrayEquals(expectedProperties, mergedPropertySources.getProperties());
}
@Test
public void emptyAnnotation() {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(startsWith("Could not detect default properties file for test"));
expectedException.expectMessage(containsString("EmptyPropertySources.properties"));
buildMergedTestPropertySources(EmptyPropertySources.class);
}
@Test
public void extendedEmptyAnnotation() {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(startsWith("Could not detect default properties file for test"));
expectedException.expectMessage(containsString("ExtendedEmptyPropertySources.properties"));
buildMergedTestPropertySources(ExtendedEmptyPropertySources.class);
}
@Test
public void value() {
assertMergedTestPropertySources(ValuePropertySources.class, new String[] { "classpath:/value.xml" },
EMPTY_STRING_ARRAY);
}
@Test
public void locationsAndValueAttributes() {
expectedException.expect(IllegalStateException.class);
buildMergedTestPropertySources(LocationsAndValuePropertySources.class);
}
@Test
public void locationsAndProperties() {
assertMergedTestPropertySources(LocationsAndPropertiesPropertySources.class, new String[] {
"classpath:/foo1.xml", "classpath:/foo2.xml" }, new String[] { "k1a=v1a", "k1b: v1b" });
}
@Test
public void inheritedLocationsAndProperties() {
assertMergedTestPropertySources(InheritedPropertySources.class, new String[] { "classpath:/foo1.xml",
"classpath:/foo2.xml" }, new String[] { "k1a=v1a", "k1b: v1b" });
}
@Test
public void extendedLocationsAndProperties() {
assertMergedTestPropertySources(ExtendedPropertySources.class, new String[] { "classpath:/foo1.xml",
"classpath:/foo2.xml", "classpath:/bar1.xml", "classpath:/bar2.xml" }, new String[] { "k1a=v1a",
"k1b: v1b", "k2a v2a", "k2b: v2b" });
}
@Test
public void overriddenLocations() {
assertMergedTestPropertySources(OverriddenLocationsPropertySources.class,
new String[] { "classpath:/baz.properties" }, new String[] { "k1a=v1a", "k1b: v1b", "key = value" });
}
@Test
public void overriddenProperties() {
assertMergedTestPropertySources(OverriddenPropertiesPropertySources.class, new String[] {
"classpath:/foo1.xml", "classpath:/foo2.xml", "classpath:/baz.properties" }, new String[] { "key = value" });
}
@Test
public void overriddenLocationsAndProperties() {
assertMergedTestPropertySources(OverriddenLocationsAndPropertiesPropertySources.class,
new String[] { "classpath:/baz.properties" }, new String[] { "key = value" });
}
// -------------------------------------------------------------------
@TestPropertySource
static class EmptyPropertySources {
}
@TestPropertySource
static class ExtendedEmptyPropertySources extends EmptyPropertySources {
}
@TestPropertySource(locations = "/foo", value = "/bar")
static class LocationsAndValuePropertySources {
}
@TestPropertySource("/value.xml")
static class ValuePropertySources {
}
@TestPropertySource(locations = { "/foo1.xml", "/foo2.xml" }, properties = { "k1a=v1a", "k1b: v1b" })
static class LocationsAndPropertiesPropertySources {
}
static class InheritedPropertySources extends LocationsAndPropertiesPropertySources {
}
@TestPropertySource(locations = { "/bar1.xml", "/bar2.xml" }, properties = { "k2a v2a", "k2b: v2b" })
static class ExtendedPropertySources extends LocationsAndPropertiesPropertySources {
}
@TestPropertySource(locations = "/baz.properties", properties = "key = value", inheritLocations = false)
static class OverriddenLocationsPropertySources extends LocationsAndPropertiesPropertySources {
}
@TestPropertySource(locations = "/baz.properties", properties = "key = value", inheritProperties = false)
static class OverriddenPropertiesPropertySources extends LocationsAndPropertiesPropertySources {
}
@TestPropertySource(locations = "/baz.properties", properties = "key = value", inheritLocations = false, inheritProperties = false)
static class OverriddenLocationsAndPropertiesPropertySources extends LocationsAndPropertiesPropertySources {
}
}

View File

@@ -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"));

View File

@@ -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"));