Provide support for context hierarchies in the TCF
Prior to this commit the Spring TestContext Framework supported creating only flat, non-hierarchical contexts. There was no easy way to create contexts with parent-child relationships. This commit addresses this issue by introducing a new @ContextHierarchy annotation that can be used in conjunction with @ContextConfiguration for declaring hierarchies of application contexts, either within a single test class or within a test class hierarchy. In addition, @DirtiesContext now supports a new 'hierarchyMode' attribute for controlling context cache clearing for context hierarchies. - Introduced a new @ContextHierarchy annotation. - Introduced 'name' attribute in @ContextConfiguration. - Introduced 'name' property in ContextConfigurationAttributes. - TestContext is now aware of @ContextHierarchy in addition to @ContextConfiguration. - Introduced findAnnotationDeclaringClassForTypes() in AnnotationUtils. - Introduced resolveContextHierarchyAttributes() in ContextLoaderUtils. - Introduced buildContextHierarchyMap() in ContextLoaderUtils. - @ContextConfiguration and @ContextHierarchy may not be used as top-level, class-level annotations simultaneously. - Introduced reference to the parent configuration in MergedContextConfiguration and WebMergedContextConfiguration. - Introduced overloaded buildMergedContextConfiguration() methods in ContextLoaderUtils in order to handle context hierarchies separately from conventional, non-hierarchical contexts. - Introduced hashCode() and equals() in ContextConfigurationAttributes. - ContextLoaderUtils ensures uniqueness of @ContextConfiguration elements within a single @ContextHierarchy declaration. - Introduced CacheAwareContextLoaderDelegate that can be used for loading contexts with transparent support for interacting with the context cache -- for example, for retrieving the parent application context in a context hierarchy. - TestContext now delegates to CacheAwareContextLoaderDelegate for loading contexts. - Introduced getParentApplicationContext() in MergedContextConfiguration - The loadContext(MergedContextConfiguration) methods in AbstractGenericContextLoader and AbstractGenericWebContextLoader now set the parent context as appropriate. - Introduced 'hierarchyMode' attribute in @DirtiesContext with a corresponding HierarchyMode enum that defines EXHAUSTIVE and CURRENT_LEVEL cache removal modes. - ContextCache now internally tracks the relationships between contexts that make up a context hierarchy. Furthermore, when a context is removed, if it is part of a context hierarchy all corresponding contexts will be removed from the cache according to the supplied HierarchyMode. - AbstractGenericWebContextLoader will set a loaded context as the ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE in the MockServletContext when context hierarchies are used if the context has no parent or if the context has a parent that is not a WAC. - Where appropriate, updated Javadoc to refer to the ServletTestExecutionListener, which was introduced in 3.2.0. - Updated Javadoc to avoid and/or suppress warnings in spring-test. - Suppressed remaining warnings in code in spring-test. Issue: SPR-5613, SPR-9863
This commit is contained in:
parent
7bc5353e07
commit
98074e7762
|
|
@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
|
|||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
|
|
@ -240,14 +241,58 @@ public abstract class AnnotationUtils {
|
|||
* if not found
|
||||
* @see Class#isAnnotationPresent(Class)
|
||||
* @see Class#getDeclaredAnnotations()
|
||||
* @see #findAnnotationDeclaringClassForTypes(List, Class)
|
||||
* @see #isAnnotationDeclaredLocally(Class, Class)
|
||||
*/
|
||||
public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, Class<?> clazz) {
|
||||
Assert.notNull(annotationType, "Annotation type must not be null");
|
||||
if (clazz == null || clazz.equals(Object.class)) {
|
||||
return null;
|
||||
}
|
||||
return (isAnnotationDeclaredLocally(annotationType, clazz)) ? clazz :
|
||||
findAnnotationDeclaringClass(annotationType, clazz.getSuperclass());
|
||||
return (isAnnotationDeclaredLocally(annotationType, clazz)) ? clazz : findAnnotationDeclaringClass(
|
||||
annotationType, clazz.getSuperclass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first {@link Class} in the inheritance hierarchy of the specified
|
||||
* {@code clazz} (including the specified {@code clazz} itself) which declares
|
||||
* at least one of the specified {@code annotationTypes}, or {@code null} if
|
||||
* none of the specified annotation types could be found.
|
||||
* <p>If the supplied {@code clazz} is {@code null}, {@code null} will be
|
||||
* returned.
|
||||
* <p>If the supplied {@code clazz} is an interface, only the interface itself
|
||||
* will be checked; the inheritance hierarchy for interfaces will not be traversed.
|
||||
* <p>The standard {@link Class} API does not provide a mechanism for determining
|
||||
* which class in an inheritance hierarchy actually declares one of several
|
||||
* candidate {@linkplain Annotation annotations}, so we need to handle this
|
||||
* explicitly.
|
||||
* @param annotationTypes the list of Class objects corresponding to the
|
||||
* annotation types
|
||||
* @param clazz the Class object corresponding to the class on which to check
|
||||
* for the annotations, or {@code null}
|
||||
* @return the first {@link Class} in the inheritance hierarchy of the specified
|
||||
* {@code clazz} which declares an annotation of at least one of the specified
|
||||
* {@code annotationTypes}, or {@code null} if not found
|
||||
* @see Class#isAnnotationPresent(Class)
|
||||
* @see Class#getDeclaredAnnotations()
|
||||
* @see #findAnnotationDeclaringClass(Class, Class)
|
||||
* @see #isAnnotationDeclaredLocally(Class, Class)
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes,
|
||||
Class<?> clazz) {
|
||||
Assert.notEmpty(annotationTypes, "The list of annotation types must not be empty");
|
||||
if (clazz == null || clazz.equals(Object.class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Class<? extends Annotation> annotationType : annotationTypes) {
|
||||
if (isAnnotationDeclaredLocally(annotationType, clazz)) {
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
|
||||
return findAnnotationDeclaringClassForTypes(annotationTypes, clazz.getSuperclass());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -348,8 +393,8 @@ public abstract class AnnotationUtils {
|
|||
* and corresponding attribute values as values
|
||||
* @since 3.1.1
|
||||
*/
|
||||
public static AnnotationAttributes getAnnotationAttributes(
|
||||
Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||
public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, boolean classValuesAsString,
|
||||
boolean nestedAnnotationsAsMap) {
|
||||
|
||||
AnnotationAttributes attrs = new AnnotationAttributes();
|
||||
Method[] methods = annotation.annotationType().getDeclaredMethods();
|
||||
|
|
@ -371,15 +416,15 @@ public abstract class AnnotationUtils {
|
|||
}
|
||||
}
|
||||
if (nestedAnnotationsAsMap && value instanceof Annotation) {
|
||||
attrs.put(method.getName(), getAnnotationAttributes(
|
||||
(Annotation)value, classValuesAsString, nestedAnnotationsAsMap));
|
||||
attrs.put(method.getName(),
|
||||
getAnnotationAttributes((Annotation) value, classValuesAsString, nestedAnnotationsAsMap));
|
||||
}
|
||||
else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
|
||||
Annotation[] realAnnotations = (Annotation[])value;
|
||||
Annotation[] realAnnotations = (Annotation[]) value;
|
||||
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
|
||||
for (int i = 0; i < realAnnotations.length; i++) {
|
||||
mappedAnnotations[i] = getAnnotationAttributes(
|
||||
realAnnotations[i], classValuesAsString, nestedAnnotationsAsMap);
|
||||
mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString,
|
||||
nestedAnnotationsAsMap);
|
||||
}
|
||||
attrs.put(method.getName(), mappedAnnotations);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -16,21 +16,26 @@
|
|||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AnnotationUtils}.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
|
|
@ -102,23 +107,91 @@ public class AnnotationUtilsTests {
|
|||
assertNull(findAnnotationDeclaringClass(Transactional.class, NonAnnotatedClass.class));
|
||||
|
||||
// inherited class-level annotation; note: @Transactional is inherited
|
||||
assertEquals(InheritedAnnotationInterface.class, findAnnotationDeclaringClass(Transactional.class,
|
||||
InheritedAnnotationInterface.class));
|
||||
assertEquals(InheritedAnnotationInterface.class,
|
||||
findAnnotationDeclaringClass(Transactional.class, InheritedAnnotationInterface.class));
|
||||
assertNull(findAnnotationDeclaringClass(Transactional.class, SubInheritedAnnotationInterface.class));
|
||||
assertEquals(InheritedAnnotationClass.class, findAnnotationDeclaringClass(Transactional.class,
|
||||
InheritedAnnotationClass.class));
|
||||
assertEquals(InheritedAnnotationClass.class, findAnnotationDeclaringClass(Transactional.class,
|
||||
SubInheritedAnnotationClass.class));
|
||||
assertEquals(InheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClass(Transactional.class, InheritedAnnotationClass.class));
|
||||
assertEquals(InheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClass(Transactional.class, SubInheritedAnnotationClass.class));
|
||||
|
||||
// non-inherited class-level annotation; note: @Order is not inherited,
|
||||
// but findAnnotationDeclaringClass() should still find it.
|
||||
assertEquals(NonInheritedAnnotationInterface.class, findAnnotationDeclaringClass(Order.class,
|
||||
NonInheritedAnnotationInterface.class));
|
||||
// but findAnnotationDeclaringClass() should still find it on classes.
|
||||
assertEquals(NonInheritedAnnotationInterface.class,
|
||||
findAnnotationDeclaringClass(Order.class, NonInheritedAnnotationInterface.class));
|
||||
assertNull(findAnnotationDeclaringClass(Order.class, SubNonInheritedAnnotationInterface.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class, findAnnotationDeclaringClass(Order.class,
|
||||
NonInheritedAnnotationClass.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class, findAnnotationDeclaringClass(Order.class,
|
||||
SubNonInheritedAnnotationClass.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClass(Order.class, NonInheritedAnnotationClass.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClass(Order.class, SubNonInheritedAnnotationClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAnnotationDeclaringClassForTypesWithSingleCandidateType() {
|
||||
|
||||
// no class-level annotation
|
||||
List<Class<? extends Annotation>> transactionalCandidateList = Arrays.<Class<? extends Annotation>> asList(Transactional.class);
|
||||
assertNull(findAnnotationDeclaringClassForTypes(transactionalCandidateList, NonAnnotatedInterface.class));
|
||||
assertNull(findAnnotationDeclaringClassForTypes(transactionalCandidateList, NonAnnotatedClass.class));
|
||||
|
||||
// inherited class-level annotation; note: @Transactional is inherited
|
||||
assertEquals(InheritedAnnotationInterface.class,
|
||||
findAnnotationDeclaringClassForTypes(transactionalCandidateList, InheritedAnnotationInterface.class));
|
||||
assertNull(findAnnotationDeclaringClassForTypes(transactionalCandidateList,
|
||||
SubInheritedAnnotationInterface.class));
|
||||
assertEquals(InheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(transactionalCandidateList, InheritedAnnotationClass.class));
|
||||
assertEquals(InheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(transactionalCandidateList, SubInheritedAnnotationClass.class));
|
||||
|
||||
// non-inherited class-level annotation; note: @Order is not inherited,
|
||||
// but findAnnotationDeclaringClassForTypes() should still find it on classes.
|
||||
List<Class<? extends Annotation>> orderCandidateList = Arrays.<Class<? extends Annotation>> asList(Order.class);
|
||||
assertEquals(NonInheritedAnnotationInterface.class,
|
||||
findAnnotationDeclaringClassForTypes(orderCandidateList, NonInheritedAnnotationInterface.class));
|
||||
assertNull(findAnnotationDeclaringClassForTypes(orderCandidateList, SubNonInheritedAnnotationInterface.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(orderCandidateList, NonInheritedAnnotationClass.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(orderCandidateList, SubNonInheritedAnnotationClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAnnotationDeclaringClassForTypesWithMultipleCandidateTypes() {
|
||||
|
||||
List<Class<? extends Annotation>> candidates = Arrays.<Class<? extends Annotation>> asList(Transactional.class,
|
||||
Order.class);
|
||||
|
||||
// no class-level annotation
|
||||
assertNull(findAnnotationDeclaringClassForTypes(candidates, NonAnnotatedInterface.class));
|
||||
assertNull(findAnnotationDeclaringClassForTypes(candidates, NonAnnotatedClass.class));
|
||||
|
||||
// inherited class-level annotation; note: @Transactional is inherited
|
||||
assertEquals(InheritedAnnotationInterface.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, InheritedAnnotationInterface.class));
|
||||
assertNull(findAnnotationDeclaringClassForTypes(candidates, SubInheritedAnnotationInterface.class));
|
||||
assertEquals(InheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, InheritedAnnotationClass.class));
|
||||
assertEquals(InheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, SubInheritedAnnotationClass.class));
|
||||
|
||||
// non-inherited class-level annotation; note: @Order is not inherited,
|
||||
// but findAnnotationDeclaringClassForTypes() should still find it on classes.
|
||||
assertEquals(NonInheritedAnnotationInterface.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, NonInheritedAnnotationInterface.class));
|
||||
assertNull(findAnnotationDeclaringClassForTypes(candidates, SubNonInheritedAnnotationInterface.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, NonInheritedAnnotationClass.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, SubNonInheritedAnnotationClass.class));
|
||||
|
||||
// class hierarchy mixed with @Transactional and @Order declarations
|
||||
assertEquals(TransactionalClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, TransactionalClass.class));
|
||||
assertEquals(TransactionalAndOrderedClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, TransactionalAndOrderedClass.class));
|
||||
assertEquals(TransactionalAndOrderedClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, SubTransactionalAndOrderedClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -216,18 +289,18 @@ public class AnnotationUtilsTests {
|
|||
}
|
||||
|
||||
|
||||
@Component(value="meta1")
|
||||
@Component(value = "meta1")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Meta1 {
|
||||
}
|
||||
|
||||
@Component(value="meta2")
|
||||
@Component(value = "meta2")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Meta2 {
|
||||
}
|
||||
|
||||
@Meta1
|
||||
@Component(value="local")
|
||||
@Component(value = "local")
|
||||
@Meta2
|
||||
static class HasLocalAndMetaComponentAnnotation {
|
||||
}
|
||||
|
|
@ -332,6 +405,16 @@ public class AnnotationUtilsTests {
|
|||
public static class SubNonInheritedAnnotationClass extends NonInheritedAnnotationClass {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public static class TransactionalClass {
|
||||
}
|
||||
|
||||
@Order
|
||||
public static class TransactionalAndOrderedClass {
|
||||
}
|
||||
|
||||
public static class SubTransactionalAndOrderedClass extends TransactionalAndOrderedClass {
|
||||
}
|
||||
|
||||
public static interface InterfaceWithAnnotatedMethod {
|
||||
|
||||
|
|
@ -353,10 +436,12 @@ public class AnnotationUtilsTests {
|
|||
}
|
||||
}
|
||||
|
||||
public abstract static class AbstractDoesNotImplementInterfaceWithAnnotatedMethod implements InterfaceWithAnnotatedMethod {
|
||||
public abstract static class AbstractDoesNotImplementInterfaceWithAnnotatedMethod implements
|
||||
InterfaceWithAnnotatedMethod {
|
||||
}
|
||||
|
||||
public static class SubOfAbstractImplementsInterfaceWithAnnotatedMethod extends AbstractDoesNotImplementInterfaceWithAnnotatedMethod {
|
||||
public static class SubOfAbstractImplementsInterfaceWithAnnotatedMethod extends
|
||||
AbstractDoesNotImplementInterfaceWithAnnotatedMethod {
|
||||
|
||||
@Override
|
||||
public void foo() {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
<config>src/test/java/org/springframework/test/context/junit4/profile/xml/DefaultProfileXmlConfigTests-context.xml</config>
|
||||
<config>src/test/java/org/springframework/test/context/junit4/aci/xml/MultipleInitializersXmlConfigTests-context.xml</config>
|
||||
<config>src/test/resources/org/springframework/test/context/web/RequestAndSessionScopedBeansWacTests-context.xml</config>
|
||||
<config>src/test/resources/org/springframework/test/context/hierarchies/web/DispatcherWacRootWacEarTests-context.xml</config>
|
||||
</configs>
|
||||
<configSets>
|
||||
</configSets>
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ import org.springframework.util.ClassUtils;
|
|||
*
|
||||
* <p>There are various choices for DataSource implementations:
|
||||
* <ul>
|
||||
* <li>SingleConnectionDataSource (using the same Connection for all getConnection calls);
|
||||
* <li>DriverManagerDataSource (creating a new Connection on each getConnection call);
|
||||
* <li>Apache's Jakarta Commons DBCP offers BasicDataSource (a real pool).
|
||||
* <li>{@code SingleConnectionDataSource} (using the same Connection for all getConnection calls)
|
||||
* <li>{@code DriverManagerDataSource} (creating a new Connection on each getConnection call)
|
||||
* <li>Apache's Jakarta Commons DBCP offers {@code org.apache.commons.dbcp.BasicDataSource} (a real pool)
|
||||
* </ul>
|
||||
*
|
||||
* <p>Typical usage in bootstrap code:
|
||||
|
|
@ -77,7 +77,6 @@ import org.springframework.util.ClassUtils;
|
|||
* @see SimpleNamingContext
|
||||
* @see org.springframework.jdbc.datasource.SingleConnectionDataSource
|
||||
* @see org.springframework.jdbc.datasource.DriverManagerDataSource
|
||||
* @see org.apache.commons.dbcp.BasicDataSource
|
||||
*/
|
||||
public class SimpleNamingContextBuilder implements InitialContextFactoryBuilder {
|
||||
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ public class MockServletContext implements ServletContext {
|
|||
* @param resourceLoader the ResourceLoader to use (or null for the default)
|
||||
* @see #registerNamedDispatcher
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public MockServletContext(String resourceBasePath, ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
|
||||
this.resourceBasePath = (resourceBasePath != null ? resourceBasePath : "");
|
||||
|
|
@ -344,6 +345,7 @@ public class MockServletContext implements ServletContext {
|
|||
* <p>Defaults to {@linkplain #COMMON_DEFAULT_SERVLET_NAME "default"}.
|
||||
* @see #setDefaultServletName
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public String getDefaultServletName() {
|
||||
return this.defaultServletName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ public abstract class AbstractDependencyInjectionSpringContextTests extends Abst
|
|||
* test instance has not been configured
|
||||
* @see #populateProtectedVariables()
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
protected void injectDependencies() throws Exception {
|
||||
Assert.state(getApplicationContext() != null,
|
||||
"injectDependencies() called without first configuring an ApplicationContext");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -36,13 +36,13 @@ import java.lang.annotation.Target;
|
|||
* mode set to {@link ClassMode#AFTER_CLASS AFTER_CLASS}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Use this annotation if a test has modified the context (for example, by
|
||||
* replacing a bean definition). Subsequent tests will be supplied a new
|
||||
* context.
|
||||
* Use this annotation if a test has modified the context — for example, by
|
||||
* replacing a bean definition or changing the state of a singleton bean.
|
||||
* Subsequent tests will be supplied a new context.
|
||||
* </p>
|
||||
* <p>
|
||||
* {@code @DirtiesContext} may be used as a class-level and
|
||||
* method-level annotation within the same class. In such scenarios, the
|
||||
* {@code @DirtiesContext} may be used as a class-level and method-level
|
||||
* annotation within the same class. In such scenarios, the
|
||||
* {@code ApplicationContext} will be marked as <em>dirty</em> after any
|
||||
* such annotated method as well as after the entire class. If the
|
||||
* {@link ClassMode} is set to {@link ClassMode#AFTER_EACH_TEST_METHOD
|
||||
|
|
@ -53,16 +53,19 @@ import java.lang.annotation.Target;
|
|||
* @author Sam Brannen
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
* @see org.springframework.test.context.ContextConfiguration
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
public @interface DirtiesContext {
|
||||
|
||||
/**
|
||||
* Defines <i>modes</i> which determine how {@code @DirtiesContext}
|
||||
* is interpreted when used to annotate a test class.
|
||||
* Defines <i>modes</i> which determine how {@code @DirtiesContext} is
|
||||
* interpreted when used to annotate a test class.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
static enum ClassMode {
|
||||
|
||||
|
|
@ -76,18 +79,64 @@ public @interface DirtiesContext {
|
|||
* The associated {@code ApplicationContext} will be marked as
|
||||
* <em>dirty</em> after each test method in the class.
|
||||
*/
|
||||
AFTER_EACH_TEST_METHOD
|
||||
AFTER_EACH_TEST_METHOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines <i>modes</i> which determine how the context cache is cleared
|
||||
* when {@code @DirtiesContext} is used in a test whose context is
|
||||
* configured as part of a hierarchy via
|
||||
* {@link org.springframework.test.context.ContextHierarchy @ContextHierarchy}.
|
||||
*
|
||||
* @since 3.2.2
|
||||
*/
|
||||
static enum HierarchyMode {
|
||||
|
||||
/**
|
||||
* The context cache will be cleared using an <em>exhaustive</em> algorithm
|
||||
* that includes not only the {@linkplain HierarchyMode#CURRENT_LEVEL current level}
|
||||
* but also all other context hierarchies that share an ancestor context
|
||||
* common to the current test.
|
||||
*
|
||||
* <p>All {@code ApplicationContexts} that reside in a subhierarchy of
|
||||
* the common ancestor context will be removed from the context cache and
|
||||
* closed.
|
||||
*/
|
||||
EXHAUSTIVE,
|
||||
|
||||
/**
|
||||
* The {@code ApplicationContext} for the <em>current level</em> in the
|
||||
* context hierarchy and all contexts in subhierarchies of the current
|
||||
* level will be removed from the context cache and closed.
|
||||
*
|
||||
* <p>The <em>current level</em> refers to the {@code ApplicationContext}
|
||||
* at the lowest level in the context hierarchy that is visible from the
|
||||
* current test.
|
||||
*/
|
||||
CURRENT_LEVEL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The <i>mode</i> to use when a test class is annotated with
|
||||
* {@code @DirtiesContext}.
|
||||
* {@code @DirtiesContext}.
|
||||
* <p>Defaults to {@link ClassMode#AFTER_CLASS AFTER_CLASS}.
|
||||
* <p>Note: Setting the class mode on an annotated test method has no meaning,
|
||||
* since the mere presence of the {@code @DirtiesContext}
|
||||
* annotation on a test method is sufficient.
|
||||
* since the mere presence of the {@code @DirtiesContext} annotation on a
|
||||
* test method is sufficient.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
ClassMode classMode() default ClassMode.AFTER_CLASS;
|
||||
|
||||
/**
|
||||
* The context cache clearing <em>mode</em> to use when a context is
|
||||
* configured as part of a hierarchy via
|
||||
* {@link org.springframework.test.context.ContextHierarchy @ContextHierarchy}.
|
||||
* <p>Defaults to {@link HierarchyMode#EXHAUSTIVE EXHAUSTIVE}.
|
||||
*
|
||||
* @since 3.2.2
|
||||
*/
|
||||
HierarchyMode hierarchyMode() default HierarchyMode.EXHAUSTIVE;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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 org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@code CacheAwareContextLoaderDelegate} loads application contexts from
|
||||
* {@link MergedContextConfiguration} by delegating to the
|
||||
* {@link ContextLoader} configured in the {@code MergedContextConfiguration}
|
||||
* and interacting transparently with the {@link ContextCache} behind the scenes.
|
||||
*
|
||||
* <p>Note: {@code CacheAwareContextLoaderDelegate} does not implement the
|
||||
* {@link ContextLoader} or {@link SmartContextLoader} interface.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public class CacheAwareContextLoaderDelegate {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(CacheAwareContextLoaderDelegate.class);
|
||||
|
||||
private final ContextCache contextCache;
|
||||
|
||||
|
||||
CacheAwareContextLoaderDelegate(ContextCache contextCache) {
|
||||
Assert.notNull(contextCache, "ContextCache must not be null");
|
||||
this.contextCache = contextCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the {@code ApplicationContext} for the supplied merged context
|
||||
* configuration. Supports both the {@link SmartContextLoader} and
|
||||
* {@link ContextLoader} SPIs.
|
||||
* @throws Exception if an error occurs while loading the application context
|
||||
*/
|
||||
private ApplicationContext loadContextInternal(MergedContextConfiguration mergedContextConfiguration)
|
||||
throws Exception {
|
||||
ContextLoader contextLoader = mergedContextConfiguration.getContextLoader();
|
||||
Assert.notNull(contextLoader, "Cannot load an ApplicationContext with a NULL 'contextLoader'. "
|
||||
+ "Consider annotating your test class with @ContextConfiguration or @ContextHierarchy.");
|
||||
|
||||
ApplicationContext applicationContext;
|
||||
|
||||
if (contextLoader instanceof SmartContextLoader) {
|
||||
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
|
||||
applicationContext = smartContextLoader.loadContext(mergedContextConfiguration);
|
||||
}
|
||||
else {
|
||||
String[] locations = mergedContextConfiguration.getLocations();
|
||||
Assert.notNull(locations, "Cannot load an ApplicationContext with a NULL 'locations' array. "
|
||||
+ "Consider annotating your test class with @ContextConfiguration or @ContextHierarchy.");
|
||||
applicationContext = contextLoader.loadContext(locations);
|
||||
}
|
||||
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the {@link ApplicationContext application context} for the supplied
|
||||
* merged context configuration.
|
||||
*
|
||||
* <p>If the context is present in the cache it will simply be returned;
|
||||
* otherwise, it will be loaded, stored in the cache, and returned.
|
||||
* @return the application context
|
||||
* @throws IllegalStateException if an error occurs while retrieving or
|
||||
* loading the application context
|
||||
*/
|
||||
public ApplicationContext loadContext(MergedContextConfiguration mergedContextConfiguration) {
|
||||
synchronized (contextCache) {
|
||||
ApplicationContext context = contextCache.get(mergedContextConfiguration);
|
||||
if (context == null) {
|
||||
try {
|
||||
context = loadContextInternal(mergedContextConfiguration);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Storing ApplicationContext in cache under key [%s].",
|
||||
mergedContextConfiguration));
|
||||
}
|
||||
contextCache.put(mergedContextConfiguration, context);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Failed to load ApplicationContext", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Retrieved ApplicationContext from cache with key [%s].",
|
||||
mergedContextConfiguration));
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -16,24 +16,30 @@
|
|||
|
||||
package org.springframework.test.context;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Cache for Spring {@link ApplicationContext ApplicationContexts} in a test environment.
|
||||
*
|
||||
* <p>Maintains a cache of {@link ApplicationContext contexts} keyed by
|
||||
* {@link MergedContextConfiguration} instances. This has significant performance
|
||||
* benefits if initializing the context would take time. While initializing a
|
||||
* Spring context itself is very quick, some beans in a context, such as a
|
||||
* {@code LocalSessionFactoryBean} for working with Hibernate, may take some time
|
||||
* to initialize. Hence it often makes sense to perform that initialization only
|
||||
* once per test suite.
|
||||
* <p>Maintains a cache of {@code ApplicationContexts} keyed by
|
||||
* {@link MergedContextConfiguration} instances.
|
||||
*
|
||||
* <p>This has significant performance benefits if initializing the context would take time.
|
||||
* While initializing a Spring context itself is very quick, some beans in a context, such
|
||||
* as a {@code LocalSessionFactoryBean} for working with Hibernate, may take some time to
|
||||
* initialize. Hence it often makes sense to perform that initialization only once per
|
||||
* test suite.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
|
|
@ -41,11 +47,22 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
class ContextCache {
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
/**
|
||||
* Map of context keys to Spring ApplicationContext instances.
|
||||
* Map of context keys to Spring {@code ApplicationContext} instances.
|
||||
*/
|
||||
private final Map<MergedContextConfiguration, ApplicationContext> contextMap =
|
||||
new ConcurrentHashMap<MergedContextConfiguration, ApplicationContext>(64);
|
||||
private final Map<MergedContextConfiguration, ApplicationContext> contextMap = new ConcurrentHashMap<MergedContextConfiguration, ApplicationContext>(
|
||||
64);
|
||||
|
||||
/**
|
||||
* Map of parent keys to sets of children keys, representing a top-down <em>tree</em>
|
||||
* of context hierarchies. This information is used for determining which subtrees
|
||||
* need to be recursively removed and closed when removing a context that is a parent
|
||||
* of other contexts.
|
||||
*/
|
||||
private final Map<MergedContextConfiguration, Set<MergedContextConfiguration>> hierarchyMap = new ConcurrentHashMap<MergedContextConfiguration, Set<MergedContextConfiguration>>(
|
||||
64);
|
||||
|
||||
private int hitCount;
|
||||
|
||||
|
|
@ -53,15 +70,18 @@ class ContextCache {
|
|||
|
||||
|
||||
/**
|
||||
* Clears all contexts from the cache.
|
||||
* Clears all contexts from the cache and clears context hierarchy information as
|
||||
* well.
|
||||
*/
|
||||
void clear() {
|
||||
this.contextMap.clear();
|
||||
synchronized (monitor) {
|
||||
this.contextMap.clear();
|
||||
this.hierarchyMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears hit and miss count statistics for the cache (i.e., resets counters
|
||||
* to zero).
|
||||
* Clears hit and miss count statistics for the cache (i.e., resets counters to zero).
|
||||
*/
|
||||
void clearStatistics() {
|
||||
this.hitCount = 0;
|
||||
|
|
@ -70,124 +90,210 @@ class ContextCache {
|
|||
|
||||
/**
|
||||
* Return whether there is a cached context for the given key.
|
||||
*
|
||||
* @param key the context key (never {@code null})
|
||||
*/
|
||||
boolean contains(MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
return this.contextMap.containsKey(key);
|
||||
synchronized (monitor) {
|
||||
return this.contextMap.containsKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a cached ApplicationContext for the given key.
|
||||
* <p>The {@link #getHitCount() hit} and {@link #getMissCount() miss}
|
||||
* counts will be updated accordingly.
|
||||
* Obtain a cached {@code ApplicationContext} for the given key.
|
||||
*
|
||||
* <p>The {@link #getHitCount() hit} and {@link #getMissCount() miss} counts will be
|
||||
* updated accordingly.
|
||||
*
|
||||
* @param key the context key (never {@code null})
|
||||
* @return the corresponding ApplicationContext instance,
|
||||
* or {@code null} if not found in the cache.
|
||||
* @return the corresponding {@code ApplicationContext} instance, or {@code null} if
|
||||
* not found in the cache.
|
||||
* @see #remove
|
||||
*/
|
||||
ApplicationContext get(MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
ApplicationContext context = this.contextMap.get(key);
|
||||
if (context == null) {
|
||||
incrementMissCount();
|
||||
synchronized (monitor) {
|
||||
ApplicationContext context = this.contextMap.get(key);
|
||||
if (context == null) {
|
||||
incrementMissCount();
|
||||
}
|
||||
else {
|
||||
incrementHitCount();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
else {
|
||||
incrementHitCount();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the hit count by one. A <em>hit</em> is an access to the
|
||||
* cache, which returned a non-null context for a queried key.
|
||||
* Increment the hit count by one. A <em>hit</em> is an access to the cache, which
|
||||
* returned a non-null context for a queried key.
|
||||
*/
|
||||
private void incrementHitCount() {
|
||||
this.hitCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the miss count by one. A <em>miss</em> is an access to the
|
||||
* cache, which returned a {@code null} context for a queried key.
|
||||
* Increment the miss count by one. A <em>miss</em> is an access to the cache, which
|
||||
* returned a {@code null} context for a queried key.
|
||||
*/
|
||||
private void incrementMissCount() {
|
||||
this.missCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the overall hit count for this cache. A <em>hit</em> is an access
|
||||
* to the cache, which returned a non-null context for a queried key.
|
||||
* Get the overall hit count for this cache. A <em>hit</em> is an access to the cache,
|
||||
* which returned a non-null context for a queried key.
|
||||
*/
|
||||
int getHitCount() {
|
||||
return this.hitCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the overall miss count for this cache. A <em>miss</em> is an
|
||||
* access to the cache, which returned a {@code null} context for a
|
||||
* queried key.
|
||||
* Get the overall miss count for this cache. A <em>miss</em> is an access to the
|
||||
* cache, which returned a {@code null} context for a queried key.
|
||||
*/
|
||||
int getMissCount() {
|
||||
return this.missCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly add an ApplicationContext instance to the cache under the given key.
|
||||
* Explicitly add an {@code ApplicationContext} instance to the cache under the given
|
||||
* key.
|
||||
*
|
||||
* @param key the context key (never {@code null})
|
||||
* @param context the ApplicationContext instance (never {@code null})
|
||||
* @param context the {@code ApplicationContext} instance (never {@code null})
|
||||
*/
|
||||
void put(MergedContextConfiguration key, ApplicationContext context) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
Assert.notNull(context, "ApplicationContext must not be null");
|
||||
this.contextMap.put(key, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the context with the given key.
|
||||
* @param key the context key (never {@code null})
|
||||
* @return the corresponding ApplicationContext instance, or {@code null}
|
||||
* if not found in the cache.
|
||||
* @see #setDirty
|
||||
*/
|
||||
ApplicationContext remove(MergedContextConfiguration key) {
|
||||
return this.contextMap.remove(key);
|
||||
}
|
||||
synchronized (monitor) {
|
||||
this.contextMap.put(key, context);
|
||||
|
||||
/**
|
||||
* Mark the context with the given key as dirty, effectively
|
||||
* {@link #remove removing} the context from the cache and explicitly
|
||||
* {@link ConfigurableApplicationContext#close() closing} it if it is an
|
||||
* instance of {@link ConfigurableApplicationContext}.
|
||||
* <p>Generally speaking, you would only call this method if you change the
|
||||
* state of a singleton bean, potentially affecting future interaction with
|
||||
* the context.
|
||||
* @param key the context key (never {@code null})
|
||||
* @see #remove
|
||||
*/
|
||||
void setDirty(MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
ApplicationContext context = remove(key);
|
||||
if (context instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) context).close();
|
||||
MergedContextConfiguration child = key;
|
||||
MergedContextConfiguration parent = child.getParent();
|
||||
while (parent != null) {
|
||||
Set<MergedContextConfiguration> list = hierarchyMap.get(parent);
|
||||
if (list == null) {
|
||||
list = new HashSet<MergedContextConfiguration>();
|
||||
hierarchyMap.put(parent, list);
|
||||
}
|
||||
list.add(child);
|
||||
child = parent;
|
||||
parent = child.getParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the number of contexts currently stored in the cache. If the
|
||||
* cache contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
|
||||
* <tt>Integer.MAX_VALUE</tt>.
|
||||
* Remove the context with the given key from the cache and explicitly
|
||||
* {@linkplain ConfigurableApplicationContext#close() close} it if it is an
|
||||
* instance of {@link ConfigurableApplicationContext}.
|
||||
*
|
||||
* <p>Generally speaking, you would only call this method if you change the
|
||||
* state of a singleton bean, potentially affecting future interaction with
|
||||
* the context.
|
||||
*
|
||||
* <p>In addition, the semantics of the supplied {@code HierarchyMode} will
|
||||
* be honored. See the Javadoc for {@link HierarchyMode} for details.
|
||||
*
|
||||
* @param key the context key; never {@code null}
|
||||
* @param hierarchyMode the hierarchy mode; may be {@code null} if the context
|
||||
* is not part of a hierarchy
|
||||
*/
|
||||
int size() {
|
||||
return this.contextMap.size();
|
||||
void remove(MergedContextConfiguration key, HierarchyMode hierarchyMode) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
|
||||
// startKey is the level at which to begin clearing the cache, depending
|
||||
// on the configured hierarchy mode.
|
||||
MergedContextConfiguration startKey = key;
|
||||
if (hierarchyMode == HierarchyMode.EXHAUSTIVE) {
|
||||
while (startKey.getParent() != null) {
|
||||
startKey = startKey.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (monitor) {
|
||||
final List<MergedContextConfiguration> removedContexts = new ArrayList<MergedContextConfiguration>();
|
||||
|
||||
remove(removedContexts, startKey);
|
||||
|
||||
// Remove all remaining references to any removed contexts from the
|
||||
// hierarchy map.
|
||||
for (MergedContextConfiguration currentKey : removedContexts) {
|
||||
for (Set<MergedContextConfiguration> children : hierarchyMap.values()) {
|
||||
children.remove(currentKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove empty entries from the hierarchy map.
|
||||
for (MergedContextConfiguration currentKey : hierarchyMap.keySet()) {
|
||||
if (hierarchyMap.get(currentKey).isEmpty()) {
|
||||
hierarchyMap.remove(currentKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(List<MergedContextConfiguration> removedContexts, MergedContextConfiguration key) {
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
|
||||
synchronized (monitor) {
|
||||
Set<MergedContextConfiguration> children = hierarchyMap.get(key);
|
||||
if (children != null) {
|
||||
for (MergedContextConfiguration child : children) {
|
||||
// Recurse through lower levels
|
||||
remove(removedContexts, child);
|
||||
}
|
||||
// Remove the set of children for the current context from the
|
||||
// hierarchy map.
|
||||
hierarchyMap.remove(key);
|
||||
}
|
||||
|
||||
// Physically remove and close leaf nodes first (i.e., on the way back up the
|
||||
// stack as opposed to prior to the recursive call).
|
||||
ApplicationContext context = contextMap.remove(key);
|
||||
if (context instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) context).close();
|
||||
}
|
||||
removedContexts.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a text string, which contains the {@link #size() size} as well
|
||||
* as the {@link #hitCount hit} and {@link #missCount miss} counts.
|
||||
* Determine the number of contexts currently stored in the cache. If the cache
|
||||
* contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
|
||||
* <tt>Integer.MAX_VALUE</tt>.
|
||||
*/
|
||||
int size() {
|
||||
synchronized (monitor) {
|
||||
return this.contextMap.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the number of parent contexts currently tracked within the cache.
|
||||
*/
|
||||
int getParentContextCount() {
|
||||
synchronized (monitor) {
|
||||
return this.hierarchyMap.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a text string, which contains the {@linkplain #size() size} as well
|
||||
* as the {@linkplain #getHitCount() hit}, {@linkplain #getMissCount() miss}, and
|
||||
* {@linkplain #getParentContextCount() parent context} counts.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("size", size()).append("hitCount", getHitCount()).
|
||||
append("missCount", getMissCount()).toString();
|
||||
return new ToStringCreator(this)//
|
||||
.append("size", size())//
|
||||
.append("hitCount", getHitCount())//
|
||||
.append("missCount", getMissCount())//
|
||||
.append("parentContextCount", getParentContextCount())//
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -66,11 +66,12 @@ import org.springframework.context.ConfigurableApplicationContext;
|
|||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see ContextHierarchy
|
||||
* @see ActiveProfiles
|
||||
* @see ContextLoader
|
||||
* @see SmartContextLoader
|
||||
* @see ContextConfigurationAttributes
|
||||
* @see MergedContextConfiguration
|
||||
* @see ActiveProfiles
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
*/
|
||||
@Documented
|
||||
|
|
@ -283,4 +284,19 @@ public @interface ContextConfiguration {
|
|||
*/
|
||||
Class<? extends ContextLoader> loader() default ContextLoader.class;
|
||||
|
||||
/**
|
||||
* The name of the context hierarchy level represented by this configuration.
|
||||
*
|
||||
* <p>If not specified the name will be inferred based on the numerical level within all
|
||||
* declared contexts within the hierarchy.
|
||||
*
|
||||
* <p>This attribute is only applicable when used within a test class hierarchy that is
|
||||
* configured using {@link ContextHierarchy @ContextHierarchy}, in which case the name
|
||||
* can be used for merging or overriding this configuration with configuration of the
|
||||
* same name in hierarchy levels defined in superclasses.
|
||||
*
|
||||
* @since 3.2.2
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.test.context;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
|
|
@ -24,6 +26,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
|||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@code ContextConfigurationAttributes} encapsulates the context
|
||||
|
|
@ -54,6 +57,8 @@ public class ContextConfigurationAttributes {
|
|||
|
||||
private final boolean inheritInitializers;
|
||||
|
||||
private final String name;
|
||||
|
||||
|
||||
/**
|
||||
* Resolve resource locations from the {@link ContextConfiguration#locations() locations}
|
||||
|
|
@ -75,7 +80,8 @@ public class ContextConfigurationAttributes {
|
|||
ObjectUtils.nullSafeToString(valueLocations), ObjectUtils.nullSafeToString(locations));
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
} else if (!ObjectUtils.isEmpty(valueLocations)) {
|
||||
}
|
||||
else if (!ObjectUtils.isEmpty(valueLocations)) {
|
||||
locations = valueLocations;
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +98,7 @@ public class ContextConfigurationAttributes {
|
|||
public ContextConfigurationAttributes(Class<?> declaringClass, ContextConfiguration contextConfiguration) {
|
||||
this(declaringClass, resolveLocations(declaringClass, contextConfiguration), contextConfiguration.classes(),
|
||||
contextConfiguration.inheritLocations(), contextConfiguration.initializers(),
|
||||
contextConfiguration.inheritInitializers(), contextConfiguration.loader());
|
||||
contextConfiguration.inheritInitializers(), contextConfiguration.name(), contextConfiguration.loader());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -109,13 +115,13 @@ public class ContextConfigurationAttributes {
|
|||
* @throws IllegalArgumentException if the {@code declaringClass} or {@code contextLoaderClass} is
|
||||
* {@code null}, or if the {@code locations} and {@code classes} are both non-empty
|
||||
* @deprecated as of Spring 3.2, use
|
||||
* {@link #ContextConfigurationAttributes(Class, String[], Class[], boolean, Class[], boolean, Class)}
|
||||
* {@link #ContextConfigurationAttributes(Class, String[], Class[], boolean, Class[], boolean, String, Class)}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public ContextConfigurationAttributes(Class<?> declaringClass, String[] locations, Class<?>[] classes,
|
||||
boolean inheritLocations, Class<? extends ContextLoader> contextLoaderClass) {
|
||||
this(declaringClass, locations, classes, inheritLocations, null, true, contextLoaderClass);
|
||||
this(declaringClass, locations, classes, inheritLocations, null, true, null, contextLoaderClass);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -138,6 +144,31 @@ public class ContextConfigurationAttributes {
|
|||
boolean inheritLocations,
|
||||
Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[] initializers,
|
||||
boolean inheritInitializers, Class<? extends ContextLoader> contextLoaderClass) {
|
||||
this(declaringClass, locations, classes, inheritLocations, initializers, inheritInitializers, null,
|
||||
contextLoaderClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link ContextConfigurationAttributes} instance for the
|
||||
* {@linkplain Class test class} that declared the
|
||||
* {@link ContextConfiguration @ContextConfiguration} annotation and its
|
||||
* corresponding attributes.
|
||||
*
|
||||
* @param declaringClass the test class that declared {@code @ContextConfiguration}
|
||||
* @param locations the resource locations declared via {@code @ContextConfiguration}
|
||||
* @param classes the annotated classes declared via {@code @ContextConfiguration}
|
||||
* @param inheritLocations the {@code inheritLocations} flag declared via {@code @ContextConfiguration}
|
||||
* @param initializers the context initializers declared via {@code @ContextConfiguration}
|
||||
* @param inheritInitializers the {@code inheritInitializers} flag declared via {@code @ContextConfiguration}
|
||||
* @param name the name of level in the context hierarchy, or {@code null} if not applicable
|
||||
* @param contextLoaderClass the {@code ContextLoader} class declared via {@code @ContextConfiguration}
|
||||
* @throws IllegalArgumentException if the {@code declaringClass} or {@code contextLoaderClass} is
|
||||
* {@code null}, or if the {@code locations} and {@code classes} are both non-empty
|
||||
*/
|
||||
public ContextConfigurationAttributes(Class<?> declaringClass, String[] locations, Class<?>[] classes,
|
||||
boolean inheritLocations,
|
||||
Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[] initializers,
|
||||
boolean inheritInitializers, String name, Class<? extends ContextLoader> contextLoaderClass) {
|
||||
|
||||
Assert.notNull(declaringClass, "declaringClass must not be null");
|
||||
Assert.notNull(contextLoaderClass, "contextLoaderClass must not be null");
|
||||
|
|
@ -158,6 +189,7 @@ public class ContextConfigurationAttributes {
|
|||
this.inheritLocations = inheritLocations;
|
||||
this.initializers = initializers;
|
||||
this.inheritInitializers = inheritInitializers;
|
||||
this.name = StringUtils.hasText(name) ? name : null;
|
||||
this.contextLoaderClass = contextLoaderClass;
|
||||
}
|
||||
|
||||
|
|
@ -305,6 +337,101 @@ public class ContextConfigurationAttributes {
|
|||
return contextLoaderClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the context hierarchy level that was declared via
|
||||
* {@link ContextConfiguration @ContextConfiguration}.
|
||||
*
|
||||
* @return the name of the context hierarchy level or {@code null} if not applicable
|
||||
* @see ContextConfiguration#name()
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique hash code for all properties of this
|
||||
* {@code ContextConfigurationAttributes} instance excluding the
|
||||
* {@linkplain #getName() name}.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + declaringClass.hashCode();
|
||||
result = prime * result + Arrays.hashCode(locations);
|
||||
result = prime * result + Arrays.hashCode(classes);
|
||||
result = prime * result + (inheritLocations ? 1231 : 1237);
|
||||
result = prime * result + Arrays.hashCode(initializers);
|
||||
result = prime * result + (inheritInitializers ? 1231 : 1237);
|
||||
result = prime * result + contextLoaderClass.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied object is equal to this
|
||||
* {@code ContextConfigurationAttributes} instance by comparing both object's
|
||||
* {@linkplain #getDeclaringClass() declaring class},
|
||||
* {@linkplain #getLocations() locations},
|
||||
* {@linkplain #getClasses() annotated classes},
|
||||
* {@linkplain #isInheritLocations() inheritLocations flag},
|
||||
* {@linkplain #getInitializers() context initializer classes},
|
||||
* {@linkplain #isInheritInitializers() inheritInitializers flag}, and the
|
||||
* {@link #getContextLoaderClass() ContextLoader class}.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ContextConfigurationAttributes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ContextConfigurationAttributes that = (ContextConfigurationAttributes) obj;
|
||||
|
||||
if (this.declaringClass == null) {
|
||||
if (that.declaringClass != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.declaringClass.equals(that.declaringClass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Arrays.equals(this.locations, that.locations)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Arrays.equals(this.classes, that.classes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.inheritLocations != that.inheritLocations) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Arrays.equals(this.initializers, that.initializers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.inheritInitializers != that.inheritInitializers) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.contextLoaderClass == null) {
|
||||
if (that.contextLoaderClass != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.contextLoaderClass.equals(that.contextLoaderClass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a String representation of the context configuration attributes
|
||||
* and declaring class.
|
||||
|
|
@ -318,6 +445,7 @@ public class ContextConfigurationAttributes {
|
|||
.append("inheritLocations", inheritLocations)//
|
||||
.append("initializers", ObjectUtils.nullSafeToString(initializers))//
|
||||
.append("inheritInitializers", inheritInitializers)//
|
||||
.append("name", name)//
|
||||
.append("contextLoaderClass", contextLoaderClass.getName())//
|
||||
.toString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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 @ContextHierarchy} is a class-level annotation that is used to define
|
||||
* a hierarchy of {@link org.springframework.context.ApplicationContext
|
||||
* ApplicationContexts} for integration tests.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
* @see ContextConfiguration
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ContextHierarchy {
|
||||
|
||||
/**
|
||||
* A list of {@link ContextConfiguration @ContextConfiguration} instances,
|
||||
* each of which defines a level in the context hierarchy.
|
||||
*
|
||||
* <p>If you need to merge or override the configuration for a given level
|
||||
* of the context hierarchy within a test class hierarchy, you must explicitly
|
||||
* name that level by supplying the same value to the {@link ContextConfiguration#name
|
||||
* name} attribute in {@code @ContextConfiguration} at each level in the
|
||||
* class hierarchy.
|
||||
*/
|
||||
ContextConfiguration[] value();
|
||||
|
||||
}
|
||||
|
|
@ -16,20 +16,24 @@
|
|||
|
||||
package org.springframework.test.context;
|
||||
|
||||
import static org.springframework.beans.BeanUtils.*;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
import static org.springframework.beans.BeanUtils.instantiateClass;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.findAnnotationDeclaringClass;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.findAnnotationDeclaringClassForTypes;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.isAnnotationDeclaredLocally;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
|
@ -52,10 +56,13 @@ import org.springframework.util.StringUtils;
|
|||
* @see ContextConfigurationAttributes
|
||||
* @see ActiveProfiles
|
||||
* @see ApplicationContextInitializer
|
||||
* @see ContextHierarchy
|
||||
* @see MergedContextConfiguration
|
||||
*/
|
||||
abstract class ContextLoaderUtils {
|
||||
|
||||
static final String GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX = "ContextHierarchyLevel#";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ContextLoaderUtils.class);
|
||||
|
||||
private static final String DEFAULT_CONTEXT_LOADER_CLASS_NAME = "org.springframework.test.context.support.DelegatingSmartContextLoader";
|
||||
|
|
@ -70,30 +77,29 @@ abstract class ContextLoaderUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Resolve the {@link ContextLoader} {@linkplain Class class} to use for the
|
||||
* supplied list of {@link ContextConfigurationAttributes} and then
|
||||
* instantiate and return that {@code ContextLoader}.
|
||||
* Resolve the {@link ContextLoader} {@linkplain Class class} to use for the supplied
|
||||
* list of {@link ContextConfigurationAttributes} and then instantiate and return that
|
||||
* {@code ContextLoader}.
|
||||
*
|
||||
* <p>If the supplied {@code defaultContextLoaderClassName} is
|
||||
* {@code null} or <em>empty</em>, depending on the absence or presence
|
||||
* of {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}
|
||||
* either {@value #DEFAULT_CONTEXT_LOADER_CLASS_NAME}
|
||||
* or {@value #DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME} will be used as the
|
||||
* default context loader class name. For details on the class resolution
|
||||
* process, see {@link #resolveContextLoaderClass}.
|
||||
* <p>If the supplied {@code defaultContextLoaderClassName} is {@code null} or
|
||||
* <em>empty</em>, depending on the absence or presence of
|
||||
* {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration} either
|
||||
* {@code "org.springframework.test.context.support.DelegatingSmartContextLoader"} or
|
||||
* {@code "org.springframework.test.context.web.WebDelegatingSmartContextLoader"} will
|
||||
* be used as the default context loader class name. For details on the class
|
||||
* resolution process, see {@link #resolveContextLoaderClass}.
|
||||
*
|
||||
* @param testClass the test class for which the {@code ContextLoader}
|
||||
* should be resolved; must not be {@code null}
|
||||
* @param configAttributesList the list of configuration attributes to process;
|
||||
* must not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
|
||||
* @param testClass the test class for which the {@code ContextLoader} should be
|
||||
* resolved; must not be {@code null}
|
||||
* @param configAttributesList the list of configuration attributes to process; must
|
||||
* not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
|
||||
* (i.e., as if we were traversing up the class hierarchy)
|
||||
* @param defaultContextLoaderClassName the name of the default
|
||||
* {@code ContextLoader} class to use; may be {@code null} or <em>empty</em>
|
||||
* @return the resolved {@code ContextLoader} for the supplied
|
||||
* {@code testClass} (never {@code null})
|
||||
* @param defaultContextLoaderClassName the name of the default {@code ContextLoader}
|
||||
* class to use; may be {@code null} or <em>empty</em>
|
||||
* @return the resolved {@code ContextLoader} for the supplied {@code testClass}
|
||||
* (never {@code null})
|
||||
* @see #resolveContextLoaderClass
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
static ContextLoader resolveContextLoader(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributesList, String defaultContextLoaderClassName) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
|
|
@ -113,36 +119,35 @@ abstract class ContextLoaderUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Resolve the {@link ContextLoader} {@linkplain Class class} to use for the
|
||||
* supplied list of {@link ContextConfigurationAttributes}.
|
||||
* Resolve the {@link ContextLoader} {@linkplain Class class} to use for the supplied
|
||||
* list of {@link ContextConfigurationAttributes}.
|
||||
*
|
||||
* <p>Beginning with the first level in the context configuration attributes
|
||||
* hierarchy:
|
||||
* <p>Beginning with the first level in the context configuration attributes hierarchy:
|
||||
*
|
||||
* <ol>
|
||||
* <li>If the {@link ContextConfigurationAttributes#getContextLoaderClass()
|
||||
* contextLoaderClass} property of {@link ContextConfigurationAttributes} is
|
||||
* configured with an explicit class, that class will be returned.</li>
|
||||
* <li>If an explicit {@code ContextLoader} class is not specified at the
|
||||
* current level in the hierarchy, traverse to the next level in the hierarchy
|
||||
* and return to step #1.</li>
|
||||
* <li>If no explicit {@code ContextLoader} class is found after traversing
|
||||
* the hierarchy, an attempt will be made to load and return the class
|
||||
* with the supplied {@code defaultContextLoaderClassName}.</li>
|
||||
* <li>If an explicit {@code ContextLoader} class is not specified at the current
|
||||
* level in the hierarchy, traverse to the next level in the hierarchy and return to
|
||||
* step #1.</li>
|
||||
* <li>If no explicit {@code ContextLoader} class is found after traversing the
|
||||
* hierarchy, an attempt will be made to load and return the class with the supplied
|
||||
* {@code defaultContextLoaderClassName}.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param testClass the class for which to resolve the {@code ContextLoader}
|
||||
* class; must not be {@code null}; only used for logging purposes
|
||||
* @param configAttributesList the list of configuration attributes to process;
|
||||
* must not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
|
||||
* @param testClass the class for which to resolve the {@code ContextLoader} class;
|
||||
* must not be {@code null}; only used for logging purposes
|
||||
* @param configAttributesList the list of configuration attributes to process; must
|
||||
* not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
|
||||
* (i.e., as if we were traversing up the class hierarchy)
|
||||
* @param defaultContextLoaderClassName the name of the default
|
||||
* {@code ContextLoader} class to use; must not be {@code null} or empty
|
||||
* @param defaultContextLoaderClassName the name of the default {@code ContextLoader}
|
||||
* class to use; must not be {@code null} or empty
|
||||
* @return the {@code ContextLoader} class to use for the supplied test class
|
||||
* @throws IllegalArgumentException if {@code @ContextConfiguration} is not
|
||||
* <em>present</em> on the supplied test class
|
||||
* @throws IllegalStateException if the default {@code ContextLoader} class
|
||||
* could not be loaded
|
||||
* @throws IllegalStateException if the default {@code ContextLoader} class could not
|
||||
* be loaded
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static Class<? extends ContextLoader> resolveContextLoaderClass(Class<?> testClass,
|
||||
|
|
@ -184,22 +189,201 @@ abstract class ContextLoaderUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Resolve the list of {@link ContextConfigurationAttributes configuration
|
||||
* attributes} for the supplied {@link Class class} and its superclasses.
|
||||
* Convenience method for creating a {@link ContextConfigurationAttributes} instance
|
||||
* from the supplied {@link ContextConfiguration} and declaring class and then adding
|
||||
* the attributes to the supplied list.
|
||||
*/
|
||||
private static void convertContextConfigToConfigAttributesAndAddToList(ContextConfiguration contextConfiguration,
|
||||
Class<?> declaringClass, final List<ContextConfigurationAttributes> attributesList) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Retrieved @ContextConfiguration [%s] for declaring class [%s].",
|
||||
contextConfiguration, declaringClass.getName()));
|
||||
}
|
||||
|
||||
ContextConfigurationAttributes attributes = new ContextConfigurationAttributes(declaringClass,
|
||||
contextConfiguration);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Resolved context configuration attributes: " + attributes);
|
||||
}
|
||||
attributesList.add(attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the list of lists of {@linkplain ContextConfigurationAttributes context
|
||||
* configuration attributes} for the supplied {@linkplain Class test class} and its
|
||||
* superclasses, taking into account context hierarchies declared via
|
||||
* {@link ContextHierarchy @ContextHierarchy} and
|
||||
* {@link ContextConfiguration @ContextConfiguration}.
|
||||
*
|
||||
* <p>Note that the {@link ContextConfiguration#inheritLocations
|
||||
* inheritLocations} and {@link ContextConfiguration#inheritInitializers()
|
||||
* inheritInitializers} flags of {@link ContextConfiguration
|
||||
* @ContextConfiguration} will <strong>not</strong> be taken into
|
||||
* consideration. If these flags need to be honored, that must be handled
|
||||
* manually when traversing the list returned by this method.
|
||||
* <p>The outer list represents a top-down ordering of context configuration
|
||||
* attributes, where each element in the list represents the context configuration
|
||||
* declared on a given test class in the class hierarchy. Each nested list
|
||||
* contains the context configuration attributes declared either via a single
|
||||
* instance of {@code @ContextConfiguration} on the particular class or via
|
||||
* multiple instances of {@code @ContextConfiguration} declared within a
|
||||
* single {@code @ContextHierarchy} instance on the particular class.
|
||||
* Furthermore, each nested list maintains the order in which
|
||||
* {@code @ContextConfiguration} instances are declared.
|
||||
*
|
||||
* <p>Note that the {@link ContextConfiguration#inheritLocations inheritLocations} and
|
||||
* {@link ContextConfiguration#inheritInitializers() inheritInitializers} flags of
|
||||
* {@link ContextConfiguration @ContextConfiguration} will <strong>not</strong>
|
||||
* be taken into consideration. If these flags need to be honored, that must be
|
||||
* handled manually when traversing the nested lists returned by this method.
|
||||
*
|
||||
* @param testClass the class for which to resolve the context hierarchy attributes
|
||||
* (must not be {@code null})
|
||||
* @return the list of lists of configuration attributes for the specified class;
|
||||
* never {@code null}
|
||||
* @throws IllegalArgumentException if the supplied class is {@code null}; if
|
||||
* neither {@code @ContextConfiguration} nor {@code @ContextHierarchy} is
|
||||
* <em>present</em> on the supplied class; if a given class in the class hierarchy
|
||||
* declares both {@code @ContextConfiguration} and {@code @ContextHierarchy} as
|
||||
* top-level annotations; or if individual {@code @ContextConfiguration}
|
||||
* elements within a {@code @ContextHierarchy} declaration on a given class
|
||||
* in the class hierarchy do not define unique context configuration.
|
||||
*
|
||||
* @since 3.2.2
|
||||
* @see #buildContextHierarchyMap(Class)
|
||||
* @see #resolveContextConfigurationAttributes(Class)
|
||||
*/
|
||||
static List<List<ContextConfigurationAttributes>> resolveContextHierarchyAttributes(Class<?> testClass) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
|
||||
final Class<ContextConfiguration> contextConfigType = ContextConfiguration.class;
|
||||
final Class<ContextHierarchy> contextHierarchyType = ContextHierarchy.class;
|
||||
final List<Class<? extends Annotation>> annotationTypes = Arrays.asList(contextConfigType, contextHierarchyType);
|
||||
|
||||
final List<List<ContextConfigurationAttributes>> hierarchyAttributes = new ArrayList<List<ContextConfigurationAttributes>>();
|
||||
|
||||
Class<?> declaringClass = findAnnotationDeclaringClassForTypes(annotationTypes, testClass);
|
||||
Assert.notNull(declaringClass, String.format(
|
||||
"Could not find an 'annotation declaring class' for annotation type [%s] or [%s] and test class [%s]",
|
||||
contextConfigType.getName(), contextHierarchyType.getName(), testClass.getName()));
|
||||
|
||||
while (declaringClass != null) {
|
||||
|
||||
boolean contextConfigDeclaredLocally = isAnnotationDeclaredLocally(contextConfigType, declaringClass);
|
||||
boolean contextHierarchyDeclaredLocally = isAnnotationDeclaredLocally(contextHierarchyType, declaringClass);
|
||||
|
||||
if (contextConfigDeclaredLocally && contextHierarchyDeclaredLocally) {
|
||||
String msg = String.format("Test class [%s] has been configured with both @ContextConfiguration "
|
||||
+ "and @ContextHierarchy as class-level annotations. Only one of these annotations may "
|
||||
+ "be declared as a top-level annotation per test class.", declaringClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
final List<ContextConfigurationAttributes> configAttributesList = new ArrayList<ContextConfigurationAttributes>();
|
||||
|
||||
if (contextConfigDeclaredLocally) {
|
||||
ContextConfiguration contextConfiguration = declaringClass.getAnnotation(contextConfigType);
|
||||
convertContextConfigToConfigAttributesAndAddToList(contextConfiguration, declaringClass,
|
||||
configAttributesList);
|
||||
}
|
||||
else if (contextHierarchyDeclaredLocally) {
|
||||
ContextHierarchy contextHierarchy = declaringClass.getAnnotation(contextHierarchyType);
|
||||
for (ContextConfiguration contextConfiguration : contextHierarchy.value()) {
|
||||
convertContextConfigToConfigAttributesAndAddToList(contextConfiguration, declaringClass,
|
||||
configAttributesList);
|
||||
}
|
||||
|
||||
// Check for uniqueness
|
||||
Set<ContextConfigurationAttributes> configAttributesSet = new HashSet<ContextConfigurationAttributes>(
|
||||
configAttributesList);
|
||||
if (configAttributesSet.size() != configAttributesList.size()) {
|
||||
String msg = String.format("The @ContextConfiguration elements configured via "
|
||||
+ "@ContextHierarchy in test class [%s] must define unique contexts to load.",
|
||||
declaringClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This should theoretically actually never happen...
|
||||
String msg = String.format("Test class [%s] has been configured with neither @ContextConfiguration "
|
||||
+ "nor @ContextHierarchy as a class-level annotation.", declaringClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
hierarchyAttributes.add(0, configAttributesList);
|
||||
|
||||
declaringClass = findAnnotationDeclaringClassForTypes(annotationTypes, declaringClass.getSuperclass());
|
||||
}
|
||||
|
||||
return hierarchyAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a <em>context hierarchy map</em> for the supplied {@linkplain Class
|
||||
* test class} and its superclasses, taking into account context hierarchies
|
||||
* declared via {@link ContextHierarchy @ContextHierarchy} and
|
||||
* {@link ContextConfiguration @ContextConfiguration}.
|
||||
*
|
||||
* <p>Each value in the map represents the consolidated list of {@linkplain
|
||||
* ContextConfigurationAttributes context configuration attributes} for a
|
||||
* given level in the context hierarchy (potentially across the test class
|
||||
* hierarchy), keyed by the {@link ContextConfiguration#name() name} of the
|
||||
* context hierarchy level.
|
||||
*
|
||||
* <p>If a given level in the context hierarchy does not have an explicit
|
||||
* name (i.e., configured via {@link ContextConfiguration#name}), a name will
|
||||
* be generated for that hierarchy level by appending the numerical level to
|
||||
* the {@link #GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX}.
|
||||
*
|
||||
* @param testClass the class for which to resolve the context hierarchy map
|
||||
* (must not be {@code null})
|
||||
* @return a map of context configuration attributes for the context hierarchy,
|
||||
* keyed by context hierarchy level name; never {@code null}
|
||||
*
|
||||
* @since 3.2.2
|
||||
* @see #resolveContextHierarchyAttributes(Class)
|
||||
*/
|
||||
static Map<String, List<ContextConfigurationAttributes>> buildContextHierarchyMap(Class<?> testClass) {
|
||||
final Map<String, List<ContextConfigurationAttributes>> map = new LinkedHashMap<String, List<ContextConfigurationAttributes>>();
|
||||
int hierarchyLevel = 1;
|
||||
|
||||
for (List<ContextConfigurationAttributes> configAttributesList : resolveContextHierarchyAttributes(testClass)) {
|
||||
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
||||
String name = configAttributes.getName();
|
||||
|
||||
// Assign a generated name?
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + hierarchyLevel;
|
||||
}
|
||||
|
||||
// Encountered a new context hierarchy level?
|
||||
if (!map.containsKey(name)) {
|
||||
hierarchyLevel++;
|
||||
map.put(name, new ArrayList<ContextConfigurationAttributes>());
|
||||
}
|
||||
|
||||
map.get(name).add(configAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the list of {@linkplain ContextConfigurationAttributes context
|
||||
* configuration attributes} for the supplied {@linkplain Class test class} and its
|
||||
* superclasses.
|
||||
*
|
||||
* <p>Note that the {@link ContextConfiguration#inheritLocations inheritLocations} and
|
||||
* {@link ContextConfiguration#inheritInitializers() inheritInitializers} flags of
|
||||
* {@link ContextConfiguration @ContextConfiguration} will <strong>not</strong>
|
||||
* be taken into consideration. If these flags need to be honored, that must be
|
||||
* handled manually when traversing the list returned by this method.
|
||||
*
|
||||
* @param testClass the class for which to resolve the configuration attributes (must
|
||||
* not be {@code null})
|
||||
* @return the list of configuration attributes for the specified class, ordered <em>bottom-up</em>
|
||||
* (i.e., as if we were traversing up the class hierarchy); never {@code null}
|
||||
* @throws IllegalArgumentException if the supplied class is {@code null} or
|
||||
* if {@code @ContextConfiguration} is not <em>present</em> on the supplied class
|
||||
* @return the list of configuration attributes for the specified class, ordered
|
||||
* <em>bottom-up</em> (i.e., as if we were traversing up the class hierarchy);
|
||||
* never {@code null}
|
||||
* @throws IllegalArgumentException if the supplied class is {@code null} or if
|
||||
* {@code @ContextConfiguration} is not <em>present</em> on the supplied class
|
||||
*/
|
||||
static List<ContextConfigurationAttributes> resolveContextConfigurationAttributes(Class<?> testClass) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
|
|
@ -214,19 +398,7 @@ abstract class ContextLoaderUtils {
|
|||
|
||||
while (declaringClass != null) {
|
||||
ContextConfiguration contextConfiguration = declaringClass.getAnnotation(annotationType);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Retrieved @ContextConfiguration [%s] for declaring class [%s].",
|
||||
contextConfiguration, declaringClass.getName()));
|
||||
}
|
||||
|
||||
ContextConfigurationAttributes attributes = new ContextConfigurationAttributes(declaringClass,
|
||||
contextConfiguration);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Resolved context configuration attributes: " + attributes);
|
||||
}
|
||||
|
||||
attributesList.add(attributes);
|
||||
|
||||
convertContextConfigToConfigAttributesAndAddToList(contextConfiguration, declaringClass, attributesList);
|
||||
declaringClass = findAnnotationDeclaringClass(annotationType, declaringClass.getSuperclass());
|
||||
}
|
||||
|
||||
|
|
@ -234,22 +406,21 @@ abstract class ContextLoaderUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Resolve the list of merged {@code ApplicationContextInitializer} classes
|
||||
* for the supplied list of {@code ContextConfigurationAttributes}.
|
||||
* Resolve the list of merged {@code ApplicationContextInitializer} classes for the
|
||||
* supplied list of {@code ContextConfigurationAttributes}.
|
||||
*
|
||||
* <p>Note that the {@link ContextConfiguration#inheritInitializers inheritInitializers}
|
||||
* flag of {@link ContextConfiguration @ContextConfiguration} will be taken into
|
||||
* consideration. Specifically, if the {@code inheritInitializers} flag is
|
||||
* set to {@code true} for a given level in the class hierarchy represented by
|
||||
* the provided configuration attributes, context initializer classes defined
|
||||
* at the given level will be merged with those defined in higher levels
|
||||
* of the class hierarchy.
|
||||
* consideration. Specifically, if the {@code inheritInitializers} flag is set to
|
||||
* {@code true} for a given level in the class hierarchy represented by the provided
|
||||
* configuration attributes, context initializer classes defined at the given level
|
||||
* will be merged with those defined in higher levels of the class hierarchy.
|
||||
*
|
||||
* @param configAttributesList the list of configuration attributes to process;
|
||||
* must not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
|
||||
* @param configAttributesList the list of configuration attributes to process; must
|
||||
* not be {@code null} or <em>empty</em>; must be ordered <em>bottom-up</em>
|
||||
* (i.e., as if we were traversing up the class hierarchy)
|
||||
* @return the set of merged context initializer classes, including those
|
||||
* from superclasses if appropriate (never {@code null})
|
||||
* @return the set of merged context initializer classes, including those from
|
||||
* superclasses if appropriate (never {@code null})
|
||||
* @since 3.2
|
||||
*/
|
||||
static Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> resolveInitializerClasses(
|
||||
|
|
@ -278,16 +449,15 @@ abstract class ContextLoaderUtils {
|
|||
/**
|
||||
* Resolve <em>active bean definition profiles</em> for the supplied {@link Class}.
|
||||
*
|
||||
* <p>Note that the {@link ActiveProfiles#inheritProfiles inheritProfiles}
|
||||
* flag of {@link ActiveProfiles @ActiveProfiles} will be taken into
|
||||
* consideration. Specifically, if the {@code inheritProfiles} flag is
|
||||
* set to {@code true}, profiles defined in the test class will be
|
||||
* merged with those defined in superclasses.
|
||||
* <p>Note that the {@link ActiveProfiles#inheritProfiles inheritProfiles} flag of
|
||||
* {@link ActiveProfiles @ActiveProfiles} will be taken into consideration.
|
||||
* Specifically, if the {@code inheritProfiles} flag is set to {@code true}, profiles
|
||||
* defined in the test class will be merged with those defined in superclasses.
|
||||
*
|
||||
* @param testClass the class for which to resolve the active profiles (must
|
||||
* not be {@code null})
|
||||
* @return the set of active profiles for the specified class, including
|
||||
* active profiles from superclasses if appropriate (never {@code null})
|
||||
* @param testClass the class for which to resolve the active profiles (must not be
|
||||
* {@code null})
|
||||
* @return the set of active profiles for the specified class, including active
|
||||
* profiles from superclasses if appropriate (never {@code null})
|
||||
* @see ActiveProfiles
|
||||
* @see org.springframework.context.annotation.Profile
|
||||
*/
|
||||
|
|
@ -342,14 +512,72 @@ abstract class ContextLoaderUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Build the {@link MergedContextConfiguration merged context configuration}
|
||||
* for the supplied {@link Class testClass} and
|
||||
* {@code defaultContextLoaderClassName}.
|
||||
* Build the {@link MergedContextConfiguration merged context configuration} for
|
||||
* the supplied {@link Class testClass} and {@code defaultContextLoaderClassName},
|
||||
* taking into account context hierarchies declared via
|
||||
* {@link ContextHierarchy @ContextHierarchy} and
|
||||
* {@link ContextConfiguration @ContextConfiguration}.
|
||||
*
|
||||
* @param testClass the test class for which the {@code MergedContextConfiguration}
|
||||
* should be built (must not be {@code null})
|
||||
* @param defaultContextLoaderClassName the name of the default
|
||||
* {@code ContextLoader} class to use (may be {@code null})
|
||||
* @param defaultContextLoaderClassName the name of the default {@code ContextLoader}
|
||||
* class to use (may be {@code null})
|
||||
* @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate to
|
||||
* be passed to the {@code MergedContextConfiguration} constructor
|
||||
* @return the merged context configuration
|
||||
* @see #buildContextHierarchyMap(Class)
|
||||
* @see #buildMergedContextConfiguration(Class, List, String, MergedContextConfiguration, CacheAwareContextLoaderDelegate)
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
static MergedContextConfiguration buildMergedContextConfiguration(Class<?> testClass,
|
||||
String defaultContextLoaderClassName, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
|
||||
if (testClass.isAnnotationPresent(ContextHierarchy.class)) {
|
||||
Map<String, List<ContextConfigurationAttributes>> hierarchyMap = buildContextHierarchyMap(testClass);
|
||||
|
||||
MergedContextConfiguration parentConfig = null;
|
||||
MergedContextConfiguration mergedConfig = null;
|
||||
|
||||
for (List<ContextConfigurationAttributes> list : hierarchyMap.values()) {
|
||||
List<ContextConfigurationAttributes> reversedList = new ArrayList<ContextConfigurationAttributes>(list);
|
||||
Collections.reverse(reversedList);
|
||||
|
||||
// Don't use the supplied testClass; instead ensure that we are
|
||||
// building the MCC for the actual test class that declared the
|
||||
// configuration for the current level in the context hierarchy.
|
||||
Assert.notEmpty(reversedList, "ContextConfigurationAttributes list must not be empty");
|
||||
Class<?> declaringClass = reversedList.get(0).getDeclaringClass();
|
||||
|
||||
mergedConfig = buildMergedContextConfiguration(declaringClass, reversedList,
|
||||
defaultContextLoaderClassName, parentConfig, cacheAwareContextLoaderDelegate);
|
||||
parentConfig = mergedConfig;
|
||||
}
|
||||
|
||||
// Return the last level in the context hierarchy
|
||||
return mergedConfig;
|
||||
}
|
||||
else {
|
||||
return buildMergedContextConfiguration(testClass, resolveContextConfigurationAttributes(testClass),
|
||||
defaultContextLoaderClassName, null, cacheAwareContextLoaderDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the {@link MergedContextConfiguration merged context configuration} for the
|
||||
* supplied {@link Class testClass}, context configuration attributes,
|
||||
* {@code defaultContextLoaderClassName}, and parent context configuration.
|
||||
*
|
||||
* @param testClass the test class for which the {@code MergedContextConfiguration}
|
||||
* should be built (must not be {@code null})
|
||||
* @param configAttributesList the list of context configuration attributes for the
|
||||
* specified test class, ordered <em>bottom-up</em> (i.e., as if we were
|
||||
* traversing up the class hierarchy); never {@code null} or empty
|
||||
* @param defaultContextLoaderClassName the name of the default {@code ContextLoader}
|
||||
* class to use (may be {@code null})
|
||||
* @param parentConfig the merged context configuration for the parent application
|
||||
* context in a context hierarchy, or {@code null} if there is no parent
|
||||
* @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate to
|
||||
* be passed to the {@code MergedContextConfiguration} constructor
|
||||
* @return the merged context configuration
|
||||
* @see #resolveContextLoader
|
||||
* @see #resolveContextConfigurationAttributes
|
||||
|
|
@ -358,10 +586,11 @@ abstract class ContextLoaderUtils {
|
|||
* @see #resolveActiveProfiles
|
||||
* @see MergedContextConfiguration
|
||||
*/
|
||||
static MergedContextConfiguration buildMergedContextConfiguration(Class<?> testClass,
|
||||
String defaultContextLoaderClassName) {
|
||||
private static MergedContextConfiguration buildMergedContextConfiguration(final Class<?> testClass,
|
||||
final List<ContextConfigurationAttributes> configAttributesList,
|
||||
final String defaultContextLoaderClassName, MergedContextConfiguration parentConfig,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
|
||||
|
||||
final List<ContextConfigurationAttributes> configAttributesList = resolveContextConfigurationAttributes(testClass);
|
||||
final ContextLoader contextLoader = resolveContextLoader(testClass, configAttributesList,
|
||||
defaultContextLoaderClassName);
|
||||
final List<String> locationsList = new ArrayList<String>();
|
||||
|
|
@ -397,22 +626,21 @@ abstract class ContextLoaderUtils {
|
|||
String[] activeProfiles = resolveActiveProfiles(testClass);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildWebMergedContextConfiguration(testClass, locations, classes,
|
||||
initializerClasses, activeProfiles, contextLoader);
|
||||
initializerClasses, activeProfiles, contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||
|
||||
if (mergedConfig == null) {
|
||||
mergedConfig = new MergedContextConfiguration(testClass, locations, classes, initializerClasses,
|
||||
activeProfiles, contextLoader);
|
||||
activeProfiles, contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||
}
|
||||
|
||||
return mergedConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}
|
||||
* Load the {@link org.springframework.test.context.web.WebAppConfiguration}
|
||||
* class, using reflection in order to avoid package cycles.
|
||||
*
|
||||
* @return the {@code @WebAppConfiguration} class or {@code null} if it
|
||||
* cannot be loaded
|
||||
* @return the {@code @WebAppConfiguration} class or {@code null} if it cannot be loaded
|
||||
* @since 3.2
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -431,12 +659,10 @@ abstract class ContextLoaderUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Attempt to build a {@link org.springframework.test.context.web.WebMergedContextConfiguration
|
||||
* WebMergedContextConfiguration} from the supplied arguments, using reflection
|
||||
* in order to avoid package cycles.
|
||||
* Attempt to build a {@link org.springframework.test.context.web.WebMergedContextConfiguration}
|
||||
* from the supplied arguments, using reflection in order to avoid package cycles.
|
||||
*
|
||||
* @return the {@code WebMergedContextConfiguration} or {@code null} if
|
||||
* it could not be built
|
||||
* @return the {@code WebMergedContextConfiguration} or {@code null} if it could not be built
|
||||
* @since 3.2
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -445,7 +671,8 @@ abstract class ContextLoaderUtils {
|
|||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses,
|
||||
String[] activeProfiles, ContextLoader contextLoader) {
|
||||
String[] activeProfiles, ContextLoader contextLoader,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parentConfig) {
|
||||
|
||||
Class<? extends Annotation> webAppConfigClass = loadWebAppConfigurationClass();
|
||||
|
||||
|
|
@ -459,11 +686,12 @@ abstract class ContextLoaderUtils {
|
|||
|
||||
Constructor<? extends MergedContextConfiguration> constructor = ClassUtils.getConstructorIfAvailable(
|
||||
webMergedConfigClass, Class.class, String[].class, Class[].class, Set.class, String[].class,
|
||||
String.class, ContextLoader.class);
|
||||
String.class, ContextLoader.class, CacheAwareContextLoaderDelegate.class,
|
||||
MergedContextConfiguration.class);
|
||||
|
||||
if (constructor != null) {
|
||||
return instantiateClass(constructor, testClass, locations, classes, initializerClasses,
|
||||
activeProfiles, resourceBasePath, contextLoader);
|
||||
activeProfiles, resourceBasePath, contextLoader, cacheAwareContextLoaderDelegate, parentConfig);
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -23,9 +23,11 @@ import java.util.Set;
|
|||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
|
@ -72,6 +74,8 @@ public class MergedContextConfiguration implements Serializable {
|
|||
private final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses;
|
||||
private final String[] activeProfiles;
|
||||
private final ContextLoader contextLoader;
|
||||
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
|
||||
private final MergedContextConfiguration parent;
|
||||
|
||||
|
||||
private static String[] processLocations(String[] locations) {
|
||||
|
|
@ -149,6 +153,7 @@ public class MergedContextConfiguration implements Serializable {
|
|||
* @param contextInitializerClasses the merged context initializer classes
|
||||
* @param activeProfiles the merged active bean definition profiles
|
||||
* @param contextLoader the resolved {@code ContextLoader}
|
||||
* @see #MergedContextConfiguration(Class, String[], Class[], Set, String[], ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)
|
||||
*/
|
||||
public MergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
|
|
@ -156,12 +161,48 @@ public class MergedContextConfiguration implements Serializable {
|
|||
Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
|
||||
String[] activeProfiles, ContextLoader contextLoader) {
|
||||
this(testClass, locations, classes, contextInitializerClasses, activeProfiles, contextLoader, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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 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
|
||||
*/
|
||||
public MergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
|
||||
String[] activeProfiles, ContextLoader contextLoader,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
|
||||
this.testClass = testClass;
|
||||
this.locations = processLocations(locations);
|
||||
this.classes = processClasses(classes);
|
||||
this.contextInitializerClasses = processContextInitializerClasses(contextInitializerClasses);
|
||||
this.activeProfiles = processActiveProfiles(activeProfiles);
|
||||
this.contextLoader = contextLoader;
|
||||
this.cacheAwareContextLoaderDelegate = cacheAwareContextLoaderDelegate;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -207,6 +248,39 @@ public class MergedContextConfiguration implements Serializable {
|
|||
return contextLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link MergedContextConfiguration} for the parent application context in a
|
||||
* context hierarchy.
|
||||
*
|
||||
* @return the parent configuration or {@code null} if there is no parent
|
||||
* @see #getParentApplicationContext()
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public MergedContextConfiguration getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent {@link ApplicationContext} for the context defined by this
|
||||
* {@code MergedContextConfiguration} from the context cache.
|
||||
* <p>
|
||||
* If the parent context has not yet been loaded, it will be loaded, stored in the
|
||||
* cache, and then returned.
|
||||
*
|
||||
* @return the parent {@code ApplicationContext} or {@code null} if there is no parent
|
||||
* @see #getParent()
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public ApplicationContext getParentApplicationContext() {
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Assert.state(cacheAwareContextLoaderDelegate != null,
|
||||
"Cannot retrieve a parent application context without access to the CacheAwareContextLoaderDelegate.");
|
||||
return cacheAwareContextLoaderDelegate.loadContext(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique hash code for all properties of this
|
||||
* {@code MergedContextConfiguration} excluding the
|
||||
|
|
@ -220,6 +294,7 @@ 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 + (parent == null ? 0 : parent.hashCode());
|
||||
result = prime * result + nullSafeToString(contextLoader).hashCode();
|
||||
return result;
|
||||
}
|
||||
|
|
@ -229,8 +304,9 @@ public class MergedContextConfiguration implements Serializable {
|
|||
* instance by comparing both object's {@linkplain #getLocations() locations},
|
||||
* {@linkplain #getClasses() annotated classes},
|
||||
* {@linkplain #getContextInitializerClasses() context initializer classes},
|
||||
* {@linkplain #getActiveProfiles() active profiles}, and the fully qualified
|
||||
* names of their {@link #getContextLoader() ContextLoaders}.
|
||||
* {@linkplain #getActiveProfiles() active profiles},
|
||||
* {@linkplain #getParent() parents}, and the fully qualified names of their
|
||||
* {@link #getContextLoader() ContextLoaders}.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
|
@ -247,15 +323,28 @@ public class MergedContextConfiguration implements Serializable {
|
|||
if (!Arrays.equals(this.locations, that.locations)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Arrays.equals(this.classes, that.classes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.contextInitializerClasses.equals(that.contextInitializerClasses)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Arrays.equals(this.activeProfiles, that.activeProfiles)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.parent == null) {
|
||||
if (that.parent != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.parent.equals(that.parent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!nullSafeToString(this.contextLoader).equals(nullSafeToString(that.contextLoader))) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -267,8 +356,9 @@ 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}, and the name of the
|
||||
* {@link #getContextLoader() ContextLoader}.
|
||||
* {@linkplain #getActiveProfiles() active profiles}, the name of the
|
||||
* {@link #getContextLoader() ContextLoader}, and the
|
||||
* {@linkplain #getParent() parent configuration}.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
@ -279,6 +369,7 @@ public class MergedContextConfiguration implements Serializable {
|
|||
.append("contextInitializerClasses", ObjectUtils.nullSafeToString(contextInitializerClasses))//
|
||||
.append("activeProfiles", ObjectUtils.nullSafeToString(activeProfiles))//
|
||||
.append("contextLoader", nullSafeToString(contextLoader))//
|
||||
.append("parent", parent)//
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -29,8 +29,7 @@ import org.springframework.context.ApplicationContext;
|
|||
* context that it loads (see {@link MergedContextConfiguration#getActiveProfiles()}
|
||||
* and {@link #loadContext(MergedContextConfiguration)}).
|
||||
*
|
||||
* <p>See the Javadoc for
|
||||
* {@link ContextConfiguration @ContextConfiguration}
|
||||
* <p>See the Javadoc for {@link ContextConfiguration @ContextConfiguration}
|
||||
* for a definition of <em>annotated class</em>.
|
||||
*
|
||||
* <p>Clients of a {@code SmartContextLoader} should call
|
||||
|
|
@ -48,8 +47,8 @@ import org.springframework.context.ApplicationContext;
|
|||
* <p>Even though {@code SmartContextLoader} extends {@code ContextLoader},
|
||||
* clients should favor {@code SmartContextLoader}-specific methods over those
|
||||
* defined in {@code ContextLoader}, particularly because a
|
||||
* {@code SmartContextLoader} may choose not to support methods defined in
|
||||
* the {@code ContextLoader} SPI.
|
||||
* {@code SmartContextLoader} may choose not to support methods defined in the
|
||||
* {@code ContextLoader} SPI.
|
||||
*
|
||||
* <p>Concrete implementations must provide a {@code public} no-args constructor.
|
||||
*
|
||||
|
|
@ -59,6 +58,9 @@ import org.springframework.context.ApplicationContext;
|
|||
* <li>{@link org.springframework.test.context.support.AnnotationConfigContextLoader AnnotationConfigContextLoader}</li>
|
||||
* <li>{@link org.springframework.test.context.support.GenericXmlContextLoader GenericXmlContextLoader}</li>
|
||||
* <li>{@link org.springframework.test.context.support.GenericPropertiesContextLoader GenericPropertiesContextLoader}</li>
|
||||
* <li>{@link org.springframework.test.context.web.WebDelegatingSmartContextLoader WebDelegatingSmartContextLoader}</li>
|
||||
* <li>{@link org.springframework.test.context.web.AnnotationConfigWebContextLoader AnnotationConfigWebContextLoader}</li>
|
||||
* <li>{@link org.springframework.test.context.web.GenericXmlWebContextLoader GenericXmlWebContextLoader}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -20,9 +20,11 @@ import java.lang.reflect.Method;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.AttributeAccessorSupport;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
@ -41,6 +43,8 @@ public class TestContext extends AttributeAccessorSupport {
|
|||
|
||||
private final ContextCache contextCache;
|
||||
|
||||
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
|
||||
|
||||
private final MergedContextConfiguration mergedContextConfiguration;
|
||||
|
||||
private final Class<?> testClass;
|
||||
|
|
@ -61,16 +65,17 @@ public class TestContext extends AttributeAccessorSupport {
|
|||
}
|
||||
|
||||
/**
|
||||
* Construct a new test context for the supplied {@link Class test class}
|
||||
* and {@link ContextCache context cache} and parse the corresponding
|
||||
* {@link ContextConfiguration @ContextConfiguration} annotation, if
|
||||
* present.
|
||||
* Construct a new test context for the supplied {@linkplain Class test class}
|
||||
* and {@linkplain ContextCache context cache} and parse the corresponding
|
||||
* {@link ContextConfiguration @ContextConfiguration} or
|
||||
* {@link ContextHierarchy @ContextHierarchy} annotation, if present.
|
||||
* <p>If the supplied class name for the default {@code ContextLoader}
|
||||
* is {@code null} or <em>empty</em> and no concrete {@code ContextLoader}
|
||||
* class is explicitly supplied via the {@code @ContextConfiguration}
|
||||
* annotation, a
|
||||
* class is explicitly supplied via {@code @ContextConfiguration}, a
|
||||
* {@link org.springframework.test.context.support.DelegatingSmartContextLoader
|
||||
* DelegatingSmartContextLoader} will be used instead.
|
||||
* DelegatingSmartContextLoader} or
|
||||
* {@link org.springframework.test.context.web.WebDelegatingSmartContextLoader
|
||||
* WebDelegatingSmartContextLoader} will be used instead.
|
||||
* @param testClass the test class for which the test context should be
|
||||
* constructed (must not be {@code null})
|
||||
* @param contextCache the context cache from which the constructed test
|
||||
|
|
@ -83,54 +88,27 @@ public class TestContext extends AttributeAccessorSupport {
|
|||
Assert.notNull(testClass, "Test class must not be null");
|
||||
Assert.notNull(contextCache, "ContextCache must not be null");
|
||||
|
||||
MergedContextConfiguration mergedContextConfiguration;
|
||||
ContextConfiguration contextConfiguration = testClass.getAnnotation(ContextConfiguration.class);
|
||||
this.testClass = testClass;
|
||||
this.contextCache = contextCache;
|
||||
this.cacheAwareContextLoaderDelegate = new CacheAwareContextLoaderDelegate(contextCache);
|
||||
|
||||
if (contextConfiguration == null) {
|
||||
MergedContextConfiguration mergedContextConfiguration;
|
||||
|
||||
if (testClass.isAnnotationPresent(ContextConfiguration.class)
|
||||
|| testClass.isAnnotationPresent(ContextHierarchy.class)) {
|
||||
mergedContextConfiguration = ContextLoaderUtils.buildMergedContextConfiguration(testClass,
|
||||
defaultContextLoaderClassName, cacheAwareContextLoaderDelegate);
|
||||
}
|
||||
else {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("@ContextConfiguration not found for class [%s]", testClass));
|
||||
logger.info(String.format(
|
||||
"Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]",
|
||||
testClass.getName()));
|
||||
}
|
||||
mergedContextConfiguration = new MergedContextConfiguration(testClass, null, null, null, null);
|
||||
}
|
||||
else {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Retrieved @ContextConfiguration [%s] for class [%s]", contextConfiguration,
|
||||
testClass));
|
||||
}
|
||||
mergedContextConfiguration = ContextLoaderUtils.buildMergedContextConfiguration(testClass,
|
||||
defaultContextLoaderClassName);
|
||||
}
|
||||
|
||||
this.contextCache = contextCache;
|
||||
this.mergedContextConfiguration = mergedContextConfiguration;
|
||||
this.testClass = testClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an {@code ApplicationContext} for this test context using the
|
||||
* configured {@code ContextLoader} and merged context configuration. Supports
|
||||
* both the {@link SmartContextLoader} and {@link ContextLoader} SPIs.
|
||||
* @throws Exception if an error occurs while loading the application context
|
||||
*/
|
||||
private ApplicationContext loadApplicationContext() throws Exception {
|
||||
ContextLoader contextLoader = mergedContextConfiguration.getContextLoader();
|
||||
Assert.notNull(contextLoader, "Cannot load an ApplicationContext with a NULL 'contextLoader'. "
|
||||
+ "Consider annotating your test class with @ContextConfiguration.");
|
||||
|
||||
ApplicationContext applicationContext;
|
||||
|
||||
if (contextLoader instanceof SmartContextLoader) {
|
||||
SmartContextLoader smartContextLoader = (SmartContextLoader) contextLoader;
|
||||
applicationContext = smartContextLoader.loadContext(mergedContextConfiguration);
|
||||
}
|
||||
else {
|
||||
String[] locations = mergedContextConfiguration.getLocations();
|
||||
Assert.notNull(locations, "Cannot load an ApplicationContext with a NULL 'locations' array. "
|
||||
+ "Consider annotating your test class with @ContextConfiguration.");
|
||||
applicationContext = contextLoader.loadContext(locations);
|
||||
}
|
||||
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -141,31 +119,7 @@ public class TestContext extends AttributeAccessorSupport {
|
|||
* application context
|
||||
*/
|
||||
public ApplicationContext getApplicationContext() {
|
||||
synchronized (contextCache) {
|
||||
ApplicationContext context = contextCache.get(mergedContextConfiguration);
|
||||
if (context == null) {
|
||||
try {
|
||||
context = loadApplicationContext();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Storing ApplicationContext for test class [%s] in cache under key [%s].", testClass,
|
||||
mergedContextConfiguration));
|
||||
}
|
||||
contextCache.put(mergedContextConfiguration, context);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Failed to load ApplicationContext", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Retrieved ApplicationContext for test class [%s] from cache with key [%s].", testClass,
|
||||
mergedContextConfiguration));
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
return cacheAwareContextLoaderDelegate.loadContext(mergedContextConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -209,15 +163,27 @@ public class TestContext extends AttributeAccessorSupport {
|
|||
}
|
||||
|
||||
/**
|
||||
* Call this method to signal that the {@link ApplicationContext application
|
||||
* context} associated with this test context is <em>dirty</em> and should
|
||||
* be reloaded. Do this if a test has modified the context (for example, by
|
||||
* replacing a bean definition).
|
||||
* Call this method to signal that the {@linkplain ApplicationContext application
|
||||
* context} associated with this test context is <em>dirty</em> and should be
|
||||
* discarded. Do this if a test has modified the context — for example,
|
||||
* by replacing a bean definition or modifying the state of a singleton bean.
|
||||
* @deprecated As of Spring 3.2.2, use {@link #markApplicationContextDirty(HierarchyMode)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void markApplicationContextDirty() {
|
||||
synchronized (contextCache) {
|
||||
contextCache.setDirty(mergedContextConfiguration);
|
||||
}
|
||||
markApplicationContextDirty((HierarchyMode) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to signal that the {@linkplain ApplicationContext application
|
||||
* context} associated with this test context is <em>dirty</em> and should be
|
||||
* discarded. Do this if a test has modified the context — for example,
|
||||
* by replacing a bean definition or modifying the state of a singleton bean.
|
||||
* @param hierarchyMode the context cache clearing mode to be applied if the
|
||||
* context is part of a hierarchy (may be {@code null})
|
||||
*/
|
||||
public void markApplicationContextDirty(HierarchyMode hierarchyMode) {
|
||||
contextCache.remove(mergedContextConfiguration, hierarchyMode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -104,15 +104,14 @@ public class TestContextManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code TestContextManager} for the specified {@link Class test class}
|
||||
* and automatically {@link #registerTestExecutionListeners registers} the
|
||||
* Constructs a new {@code TestContextManager} for the specified {@linkplain Class
|
||||
* test class} and automatically {@link #registerTestExecutionListeners registers} the
|
||||
* {@link TestExecutionListener TestExecutionListeners} configured for the test class
|
||||
* via the {@link TestExecutionListeners @TestExecutionListeners} annotation.
|
||||
* @param testClass the test class to be managed
|
||||
* @param defaultContextLoaderClassName the name of the default
|
||||
* {@code ContextLoader} class to use (may be {@code null})
|
||||
* @param defaultContextLoaderClassName the name of the default {@code ContextLoader}
|
||||
* class to use (may be {@code null})
|
||||
* @see #registerTestExecutionListeners(TestExecutionListener...)
|
||||
* @see #retrieveTestExecutionListeners(Class)
|
||||
*/
|
||||
public TestContextManager(Class<?> testClass, String defaultContextLoaderClassName) {
|
||||
this.testContext = new TestContext(testClass, contextCache, defaultContextLoaderClassName);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -17,29 +17,24 @@
|
|||
package org.springframework.test.context;
|
||||
|
||||
/**
|
||||
* {@code TestExecutionListener} defines a <em>listener</em> API for reacting to
|
||||
* test execution events published by the {@link TestContextManager} with which
|
||||
* the listener is registered.
|
||||
* <p>
|
||||
* {@code TestExecutionListener} defines a <em>listener</em> API for
|
||||
* reacting to test execution events published by the {@link TestContextManager}
|
||||
* with which the listener is registered.
|
||||
* </p>
|
||||
* <p>
|
||||
* Concrete implementations must provide a {@code public} no-args
|
||||
* constructor, so that listeners can be instantiated transparently by tools and
|
||||
* configuration mechanisms.
|
||||
* </p>
|
||||
* Concrete implementations must provide a {@code public} no-args constructor,
|
||||
* so that listeners can be instantiated transparently by tools and configuration
|
||||
* mechanisms.
|
||||
* <p>
|
||||
* Spring provides the following out-of-the-box implementations:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener
|
||||
* <li>{@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener
|
||||
* DependencyInjectionTestExecutionListener}</li>
|
||||
* <li>
|
||||
* {@link org.springframework.test.context.support.DirtiesContextTestExecutionListener
|
||||
* <li>{@link org.springframework.test.context.support.DirtiesContextTestExecutionListener
|
||||
* DirtiesContextTestExecutionListener}</li>
|
||||
* <li>
|
||||
* {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener
|
||||
* <li>{@link org.springframework.test.context.transaction.TransactionalTestExecutionListener
|
||||
* TransactionalTestExecutionListener}</li>
|
||||
* <li>{@link org.springframework.test.context.web.ServletTestExecutionListener
|
||||
* ServletTestExecutionListener}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -26,9 +26,10 @@ import java.lang.annotation.Target;
|
|||
/**
|
||||
* {@code TestExecutionListeners} defines class-level metadata for
|
||||
* configuring which {@link TestExecutionListener TestExecutionListeners} should
|
||||
* be registered with a {@link TestContextManager}. Typically,
|
||||
* {@code @TestExecutionListeners} will be used in conjunction with
|
||||
* {@link ContextConfiguration @ContextConfiguration}.
|
||||
* be registered with a {@link TestContextManager}.
|
||||
*
|
||||
* <p>Typically, {@code @TestExecutionListeners} will be used in conjunction with
|
||||
* {@link ContextConfiguration @ContextConfiguration}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
|
|
@ -43,11 +44,10 @@ import java.lang.annotation.Target;
|
|||
public @interface TestExecutionListeners {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The {@link TestExecutionListener TestExecutionListeners} to register with
|
||||
* a {@link TestContextManager}.
|
||||
* </p>
|
||||
*
|
||||
* @see org.springframework.test.context.web.ServletTestExecutionListener
|
||||
* @see org.springframework.test.context.support.DependencyInjectionTestExecutionListener
|
||||
* @see org.springframework.test.context.support.DirtiesContextTestExecutionListener
|
||||
* @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
|
||||
|
|
@ -60,10 +60,8 @@ public @interface TestExecutionListeners {
|
|||
Class<? extends TestExecutionListener>[] value() default {};
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Whether or not {@link #value() TestExecutionListeners} from superclasses
|
||||
* should be <em>inherited</em>.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value is {@code true}, which means that an annotated
|
||||
* class will <em>inherit</em> the listeners defined by an annotated
|
||||
|
|
@ -77,11 +75,12 @@ public @interface TestExecutionListeners {
|
|||
* {@code DependencyInjectionTestExecutionListener},
|
||||
* {@code DirtiesContextTestExecutionListener}, <strong>and</strong>
|
||||
* {@code TransactionalTestExecutionListener}, in that order.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
|
||||
* DirtiesContextTestExecutionListener.class })
|
||||
* @TestExecutionListeners({
|
||||
* DependencyInjectionTestExecutionListener.class,
|
||||
* DirtiesContextTestExecutionListener.class
|
||||
* })
|
||||
* public abstract class AbstractBaseTest {
|
||||
* // ...
|
||||
* }
|
||||
|
|
@ -89,14 +88,12 @@ public @interface TestExecutionListeners {
|
|||
* @TestExecutionListeners(TransactionalTestExecutionListener.class)
|
||||
* public class TransactionalTest extends AbstractBaseTest {
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* If {@code inheritListeners} is set to {@code false}, the
|
||||
* listeners for the annotated class will <em>shadow</em> and effectively
|
||||
* replace any listeners defined by a superclass.
|
||||
* </p>
|
||||
* If {@code inheritListeners} is set to {@code false}, the listeners for the
|
||||
* annotated class will <em>shadow</em> and effectively replace any listeners
|
||||
* defined by a superclass.
|
||||
*/
|
||||
boolean inheritListeners() default true;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -16,10 +16,13 @@
|
|||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
|
@ -66,6 +69,12 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
|
|||
*
|
||||
* <ul>
|
||||
* <li>Creates a {@link GenericApplicationContext} instance.</li>
|
||||
* <li>If the supplied {@code MergedContextConfiguration} references a
|
||||
* {@linkplain MergedContextConfiguration#getParent() parent configuration},
|
||||
* the corresponding {@link MergedContextConfiguration#getParentApplicationContext()
|
||||
* ApplicationContext} will be retrieved and
|
||||
* {@linkplain GenericApplicationContext#setParent(ApplicationContext) set as the parent}
|
||||
* for the context created by this method.</li>
|
||||
* <li>Calls {@link #prepareContext(GenericApplicationContext)} for backwards
|
||||
* compatibility with the {@link org.springframework.test.context.ContextLoader
|
||||
* ContextLoader} SPI.</li>
|
||||
|
|
@ -97,6 +106,11 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
|
|||
}
|
||||
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
|
||||
ApplicationContext parent = mergedConfig.getParentApplicationContext();
|
||||
if (parent != null) {
|
||||
context.setParent(parent);
|
||||
}
|
||||
prepareContext(context);
|
||||
prepareContext(context, mergedConfig);
|
||||
customizeBeanFactory(context.getDefaultListableBeanFactory());
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
|
@ -43,26 +44,47 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
|
|||
|
||||
|
||||
/**
|
||||
* Marks the {@link ApplicationContext application context} of the supplied
|
||||
* {@link TestContext test context} as
|
||||
* {@link TestContext#markApplicationContextDirty() dirty}, and sets the
|
||||
* Marks the {@linkplain ApplicationContext application context} of the supplied
|
||||
* {@linkplain TestContext test context} as
|
||||
* {@linkplain TestContext#markApplicationContextDirty() dirty}, and sets the
|
||||
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
|
||||
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context to {@code true}.
|
||||
* @param testContext the test context whose application context should
|
||||
* marked as dirty
|
||||
* @deprecated as of Spring 3.2.2, use {@link #dirtyContext(TestContext, HierarchyMode)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
protected void dirtyContext(TestContext testContext) {
|
||||
testContext.markApplicationContextDirty();
|
||||
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current test method of the supplied {@link TestContext test
|
||||
* Marks the {@linkplain ApplicationContext application context} of the supplied
|
||||
* {@linkplain TestContext test context} as {@linkplain
|
||||
* TestContext#markApplicationContextDirty(HierarchyMode) dirty} and sets the
|
||||
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
|
||||
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context to {@code true}.
|
||||
* @param testContext the test context whose application context should
|
||||
* marked as dirty
|
||||
* @param hierarchyMode the context cache clearing mode to be applied if the
|
||||
* context is part of a hierarchy; may be {@code null}
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected void dirtyContext(TestContext testContext, HierarchyMode hierarchyMode) {
|
||||
testContext.markApplicationContextDirty(hierarchyMode);
|
||||
testContext.setAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE, Boolean.TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current test method of the supplied {@linkplain TestContext test
|
||||
* context} is annotated with {@link DirtiesContext @DirtiesContext},
|
||||
* or if the test class is annotated with {@link DirtiesContext
|
||||
* @DirtiesContext} and the {@link DirtiesContext#classMode() class
|
||||
* @DirtiesContext} and the {@linkplain DirtiesContext#classMode() class
|
||||
* mode} is set to {@link ClassMode#AFTER_EACH_TEST_METHOD
|
||||
* AFTER_EACH_TEST_METHOD}, the {@link ApplicationContext application
|
||||
* AFTER_EACH_TEST_METHOD}, the {@linkplain ApplicationContext application
|
||||
* context} of the test context will be
|
||||
* {@link TestContext#markApplicationContextDirty() marked as dirty} and the
|
||||
* {@linkplain TestContext#markApplicationContextDirty() marked as dirty} and the
|
||||
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
|
||||
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to
|
||||
* {@code true}.
|
||||
|
|
@ -88,15 +110,17 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
|
|||
}
|
||||
|
||||
if (methodDirtiesContext || (classDirtiesContext && classMode == ClassMode.AFTER_EACH_TEST_METHOD)) {
|
||||
dirtyContext(testContext);
|
||||
HierarchyMode hierarchyMode = methodDirtiesContext ? testMethod.getAnnotation(annotationType).hierarchyMode()
|
||||
: classDirtiesContextAnnotation.hierarchyMode();
|
||||
dirtyContext(testContext, hierarchyMode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the test class of the supplied {@link TestContext test context} is
|
||||
* If the test class of the supplied {@linkplain TestContext test context} is
|
||||
* annotated with {@link DirtiesContext @DirtiesContext}, the
|
||||
* {@link ApplicationContext application context} of the test context will
|
||||
* be {@link TestContext#markApplicationContextDirty() marked as dirty} ,
|
||||
* {@linkplain ApplicationContext application context} of the test context will
|
||||
* be {@linkplain TestContext#markApplicationContextDirty() marked as dirty} ,
|
||||
* and the
|
||||
* {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE
|
||||
* REINJECT_DEPENDENCIES_ATTRIBUTE} in the test context will be set to
|
||||
|
|
@ -107,12 +131,15 @@ public class DirtiesContextTestExecutionListener extends AbstractTestExecutionLi
|
|||
Class<?> testClass = testContext.getTestClass();
|
||||
Assert.notNull(testClass, "The test class of the supplied TestContext must not be null");
|
||||
|
||||
boolean dirtiesContext = testClass.isAnnotationPresent(DirtiesContext.class);
|
||||
final Class<DirtiesContext> annotationType = DirtiesContext.class;
|
||||
|
||||
boolean dirtiesContext = testClass.isAnnotationPresent(annotationType);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("After test class: context [" + testContext + "], dirtiesContext [" + dirtiesContext + "].");
|
||||
}
|
||||
if (dirtiesContext) {
|
||||
dirtyContext(testContext);
|
||||
HierarchyMode hierarchyMode = testClass.getAnnotation(annotationType).hierarchyMode();
|
||||
dirtyContext(testContext, hierarchyMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -31,6 +31,7 @@ import org.springframework.core.io.ResourceLoader;
|
|||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.support.AbstractContextLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
|
||||
|
|
@ -71,6 +72,12 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
|
|||
*
|
||||
* <ul>
|
||||
* <li>Creates a {@link GenericWebApplicationContext} instance.</li>
|
||||
* <li>If the supplied {@code MergedContextConfiguration} references a
|
||||
* {@linkplain MergedContextConfiguration#getParent() parent configuration},
|
||||
* the corresponding {@link MergedContextConfiguration#getParentApplicationContext()
|
||||
* ApplicationContext} will be retrieved and
|
||||
* {@linkplain GenericWebApplicationContext#setParent(ApplicationContext) set as the parent}
|
||||
* for the context created by this method.</li>
|
||||
* <li>Delegates to {@link #configureWebResources} to create the
|
||||
* {@link MockServletContext} and set it in the {@code WebApplicationContext}.</li>
|
||||
* <li>Calls {@link #prepareContext} to allow for customizing the context
|
||||
|
|
@ -107,6 +114,11 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
|
|||
}
|
||||
|
||||
GenericWebApplicationContext context = new GenericWebApplicationContext();
|
||||
|
||||
ApplicationContext parent = mergedConfig.getParentApplicationContext();
|
||||
if (parent != null) {
|
||||
context.setParent(parent);
|
||||
}
|
||||
configureWebResources(context, webMergedConfig);
|
||||
prepareContext(context, webMergedConfig);
|
||||
customizeBeanFactory(context.getDefaultListableBeanFactory(), webMergedConfig);
|
||||
|
|
@ -119,9 +131,20 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
|
|||
}
|
||||
|
||||
/**
|
||||
* Configures web resources for the supplied web application context.
|
||||
* Configures web resources for the supplied web application context (WAC).
|
||||
*
|
||||
* <p>Implementation details:
|
||||
* <h4>Implementation Details</h4>
|
||||
*
|
||||
* <p>If the supplied WAC has no parent or its parent is not a WAC, the
|
||||
* supplied WAC will be configured as the Root WAC (see "<em>Root WAC
|
||||
* Configuration</em>" below).
|
||||
*
|
||||
* <p>Otherwise the context hierarchy of the supplied WAC will be traversed
|
||||
* to find the top-most WAC (i.e., the root); and the {@link ServletContext}
|
||||
* of the Root WAC will be set as the {@code ServletContext} for the supplied
|
||||
* WAC.
|
||||
*
|
||||
* <h4>Root WAC Configuration</h4>
|
||||
*
|
||||
* <ul>
|
||||
* <li>The resource base path is retrieved from the supplied
|
||||
|
|
@ -146,13 +169,33 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa
|
|||
protected void configureWebResources(GenericWebApplicationContext context,
|
||||
WebMergedContextConfiguration webMergedConfig) {
|
||||
|
||||
String resourceBasePath = webMergedConfig.getResourceBasePath();
|
||||
ResourceLoader resourceLoader = resourceBasePath.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX) ? new DefaultResourceLoader()
|
||||
: new FileSystemResourceLoader();
|
||||
ApplicationContext parent = context.getParent();
|
||||
|
||||
ServletContext servletContext = new MockServletContext(resourceBasePath, resourceLoader);
|
||||
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
|
||||
context.setServletContext(servletContext);
|
||||
// if the WAC has no parent or the parent is not a WAC, set the WAC as
|
||||
// the Root WAC:
|
||||
if (parent == null || (!(parent instanceof WebApplicationContext))) {
|
||||
String resourceBasePath = webMergedConfig.getResourceBasePath();
|
||||
ResourceLoader resourceLoader = resourceBasePath.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX) ? new DefaultResourceLoader()
|
||||
: new FileSystemResourceLoader();
|
||||
|
||||
ServletContext servletContext = new MockServletContext(resourceBasePath, resourceLoader);
|
||||
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
|
||||
context.setServletContext(servletContext);
|
||||
}
|
||||
else {
|
||||
ServletContext servletContext = null;
|
||||
|
||||
// find the Root WAC
|
||||
while (parent != null) {
|
||||
if (parent instanceof WebApplicationContext && !(parent.getParent() instanceof WebApplicationContext)) {
|
||||
servletContext = ((WebApplicationContext) parent).getServletContext();
|
||||
break;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
Assert.state(servletContext != null, "Failed to find Root WebApplicationContext in the context hierarchy");
|
||||
context.setServletContext(servletContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
|
|||
* @see TestExecutionListener#prepareTestInstance(TestContext)
|
||||
* @see #setUpRequestContextIfNecessary(TestContext)
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public void prepareTestInstance(TestContext testContext) throws Exception {
|
||||
setUpRequestContextIfNecessary(testContext);
|
||||
}
|
||||
|
|
@ -80,6 +81,7 @@ public class ServletTestExecutionListener extends AbstractTestExecutionListener
|
|||
* @see TestExecutionListener#beforeTestMethod(TestContext)
|
||||
* @see #setUpRequestContextIfNecessary(TestContext)
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public void beforeTestMethod(TestContext testContext) throws Exception {
|
||||
setUpRequestContextIfNecessary(testContext);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -21,6 +21,7 @@ import java.util.Set;
|
|||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
|
@ -77,7 +78,11 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
|||
* @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}
|
||||
* @see #WebMergedContextConfiguration(Class, String[], Class[], Set, String[], String, ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)
|
||||
* @deprecated as of Spring 3.2.2, use
|
||||
* {@link #WebMergedContextConfiguration(Class, String[], Class[], Set, String[], String, ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public WebMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
|
|
@ -85,7 +90,45 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
|||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
|
||||
String[] activeProfiles, String resourceBasePath, ContextLoader contextLoader) {
|
||||
|
||||
super(testClass, locations, classes, contextInitializerClasses, activeProfiles, contextLoader);
|
||||
this(testClass, locations, classes, contextInitializerClasses, activeProfiles, 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.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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) {
|
||||
|
||||
super(testClass, locations, classes, contextInitializerClasses, activeProfiles, contextLoader,
|
||||
cacheAwareContextLoaderDelegate, parent);
|
||||
|
||||
this.resourceBasePath = !StringUtils.hasText(resourceBasePath) ? "" : resourceBasePath;
|
||||
}
|
||||
|
|
@ -118,8 +161,9 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
|||
* {@linkplain #getClasses() annotated classes},
|
||||
* {@linkplain #getContextInitializerClasses() context initializer classes},
|
||||
* {@linkplain #getActiveProfiles() active profiles},
|
||||
* {@linkplain #getResourceBasePath() resource base path}, and the fully
|
||||
* qualified names of their {@link #getContextLoader() ContextLoaders}.
|
||||
* {@linkplain #getResourceBasePath() resource base path},
|
||||
* {@linkplain #getParent() parents}, and the fully qualified names of their
|
||||
* {@link #getContextLoader() ContextLoaders}.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
|
@ -141,8 +185,9 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
|||
* {@linkplain #getLocations() locations}, {@linkplain #getClasses() annotated classes},
|
||||
* {@linkplain #getContextInitializerClasses() context initializer classes},
|
||||
* {@linkplain #getActiveProfiles() active profiles},
|
||||
* {@linkplain #getResourceBasePath() resource base path}, and the name of the
|
||||
* {@link #getContextLoader() ContextLoader}.
|
||||
* {@linkplain #getResourceBasePath() resource base path}, the name of the
|
||||
* {@link #getContextLoader() ContextLoader}, and the
|
||||
* {@linkplain #getParent() parent configuration}.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
@ -154,6 +199,7 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
|||
.append("activeProfiles", ObjectUtils.nullSafeToString(getActiveProfiles()))//
|
||||
.append("resourceBasePath", getResourceBasePath())//
|
||||
.append("contextLoader", nullSafeToString(getContextLoader()))//
|
||||
.append("parent", getParent())//
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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 org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.SpringRunnerContextCacheTests.*;
|
||||
|
||||
/**
|
||||
* Integration tests for verifying proper behavior of the {@link ContextCache} in
|
||||
* conjunction with cache keys used in {@link TestContext}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
* @see SpringRunnerContextCacheTests
|
||||
*/
|
||||
public class ContextCacheTests {
|
||||
|
||||
private ContextCache contextCache = new ContextCache();
|
||||
|
||||
|
||||
@Before
|
||||
public void initialCacheState() {
|
||||
assertContextCacheStatistics(contextCache, "initial state", 0, 0, 0);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
private void assertParentContextCount(int expected) {
|
||||
assertEquals("parent context count", expected, contextCache.getParentContextCount());
|
||||
}
|
||||
|
||||
private MergedContextConfiguration getMergedContextConfiguration(TestContext testContext) {
|
||||
return (MergedContextConfiguration) ReflectionTestUtils.getField(testContext, "mergedContextConfiguration");
|
||||
}
|
||||
|
||||
private ApplicationContext loadContext(Class<?> testClass) {
|
||||
TestContext testContext = new TestContext(testClass, contextCache);
|
||||
return testContext.getApplicationContext();
|
||||
}
|
||||
|
||||
private void loadCtxAndAssertStats(Class<?> testClass, int expectedSize, int expectedHitCount, int expectedMissCount) {
|
||||
assertNotNull(loadContext(testClass));
|
||||
assertContextCacheStatistics(contextCache, testClass.getName(), expectedSize, expectedHitCount,
|
||||
expectedMissCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyCacheKeyIsBasedOnContextLoader() {
|
||||
loadCtxAndAssertStats(AnnotationConfigContextLoaderTestCase.class, 1, 0, 1);
|
||||
loadCtxAndAssertStats(AnnotationConfigContextLoaderTestCase.class, 1, 1, 1);
|
||||
loadCtxAndAssertStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 1, 2);
|
||||
loadCtxAndAssertStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 2, 2);
|
||||
loadCtxAndAssertStats(AnnotationConfigContextLoaderTestCase.class, 2, 3, 2);
|
||||
loadCtxAndAssertStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 4, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyCacheKeyIsBasedOnActiveProfiles() {
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, 1, 0, 1);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, 1, 1, 1);
|
||||
// Profiles {foo, bar} should hash to the same as {bar,foo}
|
||||
loadCtxAndAssertStats(BarFooProfilesTestCase.class, 1, 2, 1);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, 1, 3, 1);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, 1, 4, 1);
|
||||
loadCtxAndAssertStats(BarFooProfilesTestCase.class, 1, 5, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyCacheBehaviorForContextHierarchies() {
|
||||
int size = 0;
|
||||
int hits = 0;
|
||||
int misses = 0;
|
||||
|
||||
// Level 1
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel1TestCase.class, ++size, hits, ++misses);
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel1TestCase.class, size, ++hits, misses);
|
||||
|
||||
// Level 2
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel2TestCase.class, ++size /* L2 */, ++hits /* L1 */,
|
||||
++misses /* L2 */);
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel2TestCase.class, size, ++hits /* L2 */, misses);
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel2TestCase.class, size, ++hits /* L2 */, misses);
|
||||
|
||||
// Level 3-A
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel3aTestCase.class, ++size /* L3A */, ++hits /* L2 */,
|
||||
++misses /* L3A */);
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel3aTestCase.class, size, ++hits /* L3A */, misses);
|
||||
|
||||
// Level 3-B
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel3bTestCase.class, ++size /* L3B */, ++hits /* L2 */,
|
||||
++misses /* L3B */);
|
||||
loadCtxAndAssertStats(ClassHierarchyContextHierarchyLevel3bTestCase.class, size, ++hits /* L3B */, misses);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeContextHierarchyCacheLevel1() {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = new TestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = new TestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Remove Level 1
|
||||
// Should also remove Levels 2, 3-A, and 3-B, leaving nothing.
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3a).getParent().getParent(),
|
||||
HierarchyMode.CURRENT_LEVEL);
|
||||
assertContextCacheStatistics(contextCache, "removed level 1", 0, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeContextHierarchyCacheLevel1WithExhaustiveMode() {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = new TestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = new TestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Remove Level 1
|
||||
// Should also remove Levels 2, 3-A, and 3-B, leaving nothing.
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3a).getParent().getParent(),
|
||||
HierarchyMode.EXHAUSTIVE);
|
||||
assertContextCacheStatistics(contextCache, "removed level 1", 0, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeContextHierarchyCacheLevel2() {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = new TestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = new TestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Remove Level 2
|
||||
// Should also remove Levels 3-A and 3-B, leaving only Level 1 as a context in the
|
||||
// cache but also removing the Level 1 hierarchy since all children have been
|
||||
// removed.
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3a).getParent(), HierarchyMode.CURRENT_LEVEL);
|
||||
assertContextCacheStatistics(contextCache, "removed level 2", 1, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeContextHierarchyCacheLevel2WithExhaustiveMode() {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = new TestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = new TestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Remove Level 2
|
||||
// Should wipe the cache
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3a).getParent(), HierarchyMode.EXHAUSTIVE);
|
||||
assertContextCacheStatistics(contextCache, "removed level 2", 0, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeContextHierarchyCacheLevel3Then2() {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = new TestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = new TestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Remove Level 3-A
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3a), HierarchyMode.CURRENT_LEVEL);
|
||||
assertContextCacheStatistics(contextCache, "removed level 3-A", 3, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Remove Level 2
|
||||
// Should also remove Level 3-B, leaving only Level 1.
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3b).getParent(), HierarchyMode.CURRENT_LEVEL);
|
||||
assertContextCacheStatistics(contextCache, "removed level 2", 1, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeContextHierarchyCacheLevel3Then2WithExhaustiveMode() {
|
||||
|
||||
// Load Level 3-A
|
||||
TestContext testContext3a = new TestContext(ClassHierarchyContextHierarchyLevel3aTestCase.class, contextCache);
|
||||
testContext3a.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A", 3, 0, 3);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Load Level 3-B
|
||||
TestContext testContext3b = new TestContext(ClassHierarchyContextHierarchyLevel3bTestCase.class, contextCache);
|
||||
testContext3b.getApplicationContext();
|
||||
assertContextCacheStatistics(contextCache, "level 3, A and B", 4, 1, 4);
|
||||
assertParentContextCount(2);
|
||||
|
||||
// Remove Level 3-A
|
||||
// Should wipe the cache.
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3a), HierarchyMode.EXHAUSTIVE);
|
||||
assertContextCacheStatistics(contextCache, "removed level 3-A", 0, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
|
||||
// Remove Level 2
|
||||
// Should not actually do anything since the cache was cleared in the
|
||||
// previous step. So the stats should remain the same.
|
||||
contextCache.remove(getMergedContextConfiguration(testContext3b).getParent(), HierarchyMode.EXHAUSTIVE);
|
||||
assertContextCacheStatistics(contextCache, "removed level 2", 0, 1, 4);
|
||||
assertParentContextCount(0);
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
}
|
||||
|
||||
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
|
||||
private static class AnnotationConfigContextLoaderTestCase {
|
||||
}
|
||||
|
||||
@ContextConfiguration(classes = Config.class, loader = CustomAnnotationConfigContextLoader.class)
|
||||
private static class CustomAnnotationConfigContextLoaderTestCase {
|
||||
}
|
||||
|
||||
private static class CustomAnnotationConfigContextLoader extends AnnotationConfigContextLoader {
|
||||
}
|
||||
|
||||
@ActiveProfiles({ "foo", "bar" })
|
||||
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
|
||||
private static class FooBarProfilesTestCase {
|
||||
}
|
||||
|
||||
@ActiveProfiles({ "bar", "foo" })
|
||||
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
|
||||
private static class BarFooProfilesTestCase {
|
||||
}
|
||||
|
||||
@ContextHierarchy({ @ContextConfiguration })
|
||||
private static class ClassHierarchyContextHierarchyLevel1TestCase {
|
||||
|
||||
@Configuration
|
||||
static class Level1Config {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ContextHierarchy({ @ContextConfiguration })
|
||||
private static class ClassHierarchyContextHierarchyLevel2TestCase extends
|
||||
ClassHierarchyContextHierarchyLevel1TestCase {
|
||||
|
||||
@Configuration
|
||||
static class Level2Config {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ContextHierarchy({ @ContextConfiguration })
|
||||
private static class ClassHierarchyContextHierarchyLevel3aTestCase extends
|
||||
ClassHierarchyContextHierarchyLevel2TestCase {
|
||||
|
||||
@Configuration
|
||||
static class Level3aConfig {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ContextHierarchy({ @ContextConfiguration })
|
||||
private static class ClassHierarchyContextHierarchyLevel3bTestCase extends
|
||||
ClassHierarchyContextHierarchyLevel2TestCase {
|
||||
|
||||
@Configuration
|
||||
static class Level3bConfig {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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 org.junit.After;
|
||||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.JUnitCore;
|
||||
import org.junit.runner.Result;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Integration tests that verify proper behavior of {@link DirtiesContext @DirtiesContext}
|
||||
* in conjunction with context hierarchies configured via {@link ContextHierarchy @ContextHierarchy}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Tadaya Tsuyukubo
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class ContextHierarchyDirtiesContextTests {
|
||||
|
||||
private static ApplicationContext context;
|
||||
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
ContextHierarchyDirtiesContextTests.context = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void classLevelDirtiesContextWithCurrentLevelHierarchyMode() {
|
||||
runTestAndVerifyHierarchies(ClassLevelDirtiesContextWithCurrentLevelModeTestCase.class, true, true, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void classLevelDirtiesContextWithExhaustiveHierarchyMode() {
|
||||
runTestAndVerifyHierarchies(ClassLevelDirtiesContextWithExhaustiveModeTestCase.class, false, false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodLevelDirtiesContextWithCurrentLevelHierarchyMode() {
|
||||
runTestAndVerifyHierarchies(MethodLevelDirtiesContextWithCurrentLevelModeTestCase.class, true, true, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodLevelDirtiesContextWithExhaustiveHierarchyMode() {
|
||||
runTestAndVerifyHierarchies(MethodLevelDirtiesContextWithExhaustiveModeTestCase.class, false, false, false);
|
||||
}
|
||||
|
||||
private void runTestAndVerifyHierarchies(Class<? extends FooTestCase> testClass, boolean isFooContextActive,
|
||||
boolean isBarContextActive, boolean isBazContextActive) {
|
||||
|
||||
JUnitCore jUnitCore = new JUnitCore();
|
||||
Result result = jUnitCore.run(testClass);
|
||||
assertTrue("all tests passed", result.wasSuccessful());
|
||||
|
||||
assertThat(ContextHierarchyDirtiesContextTests.context, notNullValue());
|
||||
|
||||
ConfigurableApplicationContext bazContext = (ConfigurableApplicationContext) ContextHierarchyDirtiesContextTests.context;
|
||||
assertEquals("baz", bazContext.getBean("bean", String.class));
|
||||
assertThat("bazContext#isActive()", bazContext.isActive(), is(isBazContextActive));
|
||||
|
||||
ConfigurableApplicationContext barContext = (ConfigurableApplicationContext) bazContext.getParent();
|
||||
assertThat(barContext, notNullValue());
|
||||
assertEquals("bar", barContext.getBean("bean", String.class));
|
||||
assertThat("barContext#isActive()", barContext.isActive(), is(isBarContextActive));
|
||||
|
||||
ConfigurableApplicationContext fooContext = (ConfigurableApplicationContext) barContext.getParent();
|
||||
assertThat(fooContext, notNullValue());
|
||||
assertEquals("foo", fooContext.getBean("bean", String.class));
|
||||
assertThat("fooContext#isActive()", fooContext.isActive(), is(isFooContextActive));
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy(@ContextConfiguration(name = "foo"))
|
||||
static abstract class FooTestCase implements ApplicationContextAware {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public String bean() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
ContextHierarchyDirtiesContextTests.context = applicationContext;
|
||||
}
|
||||
}
|
||||
|
||||
@ContextHierarchy(@ContextConfiguration(name = "bar"))
|
||||
static abstract class BarTestCase extends FooTestCase {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public String bean() {
|
||||
return "bar";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ContextHierarchy(@ContextConfiguration(name = "baz"))
|
||||
static abstract class BazTestCase extends BarTestCase {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public String bean() {
|
||||
return "baz";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* {@link DirtiesContext} is declared at the class level, without specifying
|
||||
* the {@link DirtiesContext.HierarchyMode}.
|
||||
* <p>After running this test class, the context cache should be <em>exhaustively</em>
|
||||
* cleared beginning from the current context hierarchy, upwards to the highest
|
||||
* parent context, and then back down through all subhierarchies of the parent
|
||||
* context.
|
||||
*/
|
||||
@DirtiesContext
|
||||
public static class ClassLevelDirtiesContextWithExhaustiveModeTestCase extends BazTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link DirtiesContext} is declared at the class level, specifying the
|
||||
* {@link DirtiesContext.HierarchyMode#CURRENT_LEVEL CURRENT_LEVEL} hierarchy mode.
|
||||
* <p>After running this test class, the context cache should be cleared
|
||||
* beginning from the current context hierarchy and down through all subhierarchies.
|
||||
*/
|
||||
@DirtiesContext(hierarchyMode = HierarchyMode.CURRENT_LEVEL)
|
||||
public static class ClassLevelDirtiesContextWithCurrentLevelModeTestCase extends BazTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link DirtiesContext} is declared at the method level, without specifying
|
||||
* the {@link DirtiesContext.HierarchyMode}.
|
||||
* <p>After running this test class, the context cache should be <em>exhaustively</em>
|
||||
* cleared beginning from the current context hierarchy, upwards to the highest
|
||||
* parent context, and then back down through all subhierarchies of the parent
|
||||
* context.
|
||||
*/
|
||||
public static class MethodLevelDirtiesContextWithExhaustiveModeTestCase extends BazTestCase {
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void test() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link DirtiesContext} is declared at the method level, specifying the
|
||||
* {@link DirtiesContext.HierarchyMode#CURRENT_LEVEL CURRENT_LEVEL} hierarchy mode.
|
||||
* <p>After running this test class, the context cache should be cleared
|
||||
* beginning from the current context hierarchy and down through all subhierarchies.
|
||||
*/
|
||||
public static class MethodLevelDirtiesContextWithCurrentLevelModeTestCase extends BazTestCase {
|
||||
|
||||
@Test
|
||||
@DirtiesContext(hierarchyMode = HierarchyMode.CURRENT_LEVEL)
|
||||
public void test() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -16,13 +16,16 @@
|
|||
|
||||
package org.springframework.test.context;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.ContextLoaderUtils.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
|
@ -105,6 +108,233 @@ public class ContextLoaderUtilsTests {
|
|||
assertEquals(expectedInitializerClasses, mergedConfig.getContextInitializerClasses());
|
||||
}
|
||||
|
||||
private void debugConfigAttributes(List<ContextConfigurationAttributes> configAttributesList) {
|
||||
// for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
||||
// System.err.println(configAttributes);
|
||||
// }
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void resolveContextHierarchyAttributesForSingleTestClassWithContextConfigurationAndContextHierarchy() {
|
||||
resolveContextHierarchyAttributes(SingleTestClassWithContextConfigurationAndContextHierarchy.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveContextHierarchyAttributesForSingleTestClassWithImplicitSingleLevelContextHierarchy() {
|
||||
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(BareAnnotations.class);
|
||||
assertEquals(1, hierarchyAttributes.size());
|
||||
List<ContextConfigurationAttributes> configAttributesList = hierarchyAttributes.get(0);
|
||||
assertEquals(1, configAttributesList.size());
|
||||
debugConfigAttributes(configAttributesList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveContextHierarchyAttributesForSingleTestClassWithSingleLevelContextHierarchy() {
|
||||
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(SingleTestClassWithSingleLevelContextHierarchy.class);
|
||||
assertEquals(1, hierarchyAttributes.size());
|
||||
List<ContextConfigurationAttributes> configAttributesList = hierarchyAttributes.get(0);
|
||||
assertEquals(1, configAttributesList.size());
|
||||
debugConfigAttributes(configAttributesList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveContextHierarchyAttributesForSingleTestClassWithTripleLevelContextHierarchy() {
|
||||
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(SingleTestClassWithTripleLevelContextHierarchy.class);
|
||||
assertEquals(1, hierarchyAttributes.size());
|
||||
List<ContextConfigurationAttributes> configAttributesList = hierarchyAttributes.get(0);
|
||||
assertEquals(3, configAttributesList.size());
|
||||
debugConfigAttributes(configAttributesList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveContextHierarchyAttributesForTestClassHierarchyWithSingleLevelContextHierarchies() {
|
||||
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass3WithSingleLevelContextHierarchy.class);
|
||||
assertEquals(3, hierarchyAttributes.size());
|
||||
|
||||
List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0);
|
||||
debugConfigAttributes(configAttributesListClassLevel1);
|
||||
assertEquals(1, configAttributesListClassLevel1.size());
|
||||
assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml"));
|
||||
|
||||
List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1);
|
||||
debugConfigAttributes(configAttributesListClassLevel2);
|
||||
assertEquals(1, configAttributesListClassLevel2.size());
|
||||
assertArrayEquals(new String[] { "two-A.xml", "two-B.xml" },
|
||||
configAttributesListClassLevel2.get(0).getLocations());
|
||||
|
||||
List<ContextConfigurationAttributes> configAttributesListClassLevel3 = hierarchyAttributes.get(2);
|
||||
debugConfigAttributes(configAttributesListClassLevel3);
|
||||
assertEquals(1, configAttributesListClassLevel3.size());
|
||||
assertThat(configAttributesListClassLevel3.get(0).getLocations()[0], equalTo("three.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSubclass() {
|
||||
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSubclass.class);
|
||||
assertEquals(2, hierarchyAttributes.size());
|
||||
|
||||
List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0);
|
||||
debugConfigAttributes(configAttributesListClassLevel1);
|
||||
assertEquals(1, configAttributesListClassLevel1.size());
|
||||
assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml"));
|
||||
|
||||
List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1);
|
||||
debugConfigAttributes(configAttributesListClassLevel2);
|
||||
assertEquals(1, configAttributesListClassLevel2.size());
|
||||
assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSuperclass() {
|
||||
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSuperclass.class);
|
||||
assertEquals(2, hierarchyAttributes.size());
|
||||
|
||||
List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0);
|
||||
debugConfigAttributes(configAttributesListClassLevel1);
|
||||
assertEquals(1, configAttributesListClassLevel1.size());
|
||||
assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml"));
|
||||
|
||||
List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1);
|
||||
debugConfigAttributes(configAttributesListClassLevel2);
|
||||
assertEquals(1, configAttributesListClassLevel2.size());
|
||||
assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveContextHierarchyAttributesForTestClassHierarchyWithMultiLevelContextHierarchies() {
|
||||
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass3WithMultiLevelContextHierarchy.class);
|
||||
assertEquals(3, hierarchyAttributes.size());
|
||||
|
||||
List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0);
|
||||
debugConfigAttributes(configAttributesListClassLevel1);
|
||||
assertEquals(2, configAttributesListClassLevel1.size());
|
||||
assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("1-A.xml"));
|
||||
assertThat(configAttributesListClassLevel1.get(1).getLocations()[0], equalTo("1-B.xml"));
|
||||
|
||||
List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1);
|
||||
debugConfigAttributes(configAttributesListClassLevel2);
|
||||
assertEquals(2, configAttributesListClassLevel2.size());
|
||||
assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("2-A.xml"));
|
||||
assertThat(configAttributesListClassLevel2.get(1).getLocations()[0], equalTo("2-B.xml"));
|
||||
|
||||
List<ContextConfigurationAttributes> configAttributesListClassLevel3 = hierarchyAttributes.get(2);
|
||||
debugConfigAttributes(configAttributesListClassLevel3);
|
||||
assertEquals(3, configAttributesListClassLevel3.size());
|
||||
assertThat(configAttributesListClassLevel3.get(0).getLocations()[0], equalTo("3-A.xml"));
|
||||
assertThat(configAttributesListClassLevel3.get(1).getLocations()[0], equalTo("3-B.xml"));
|
||||
assertThat(configAttributesListClassLevel3.get(2).getLocations()[0], equalTo("3-C.xml"));
|
||||
}
|
||||
|
||||
private void assertContextConfigEntriesAreNotUnique(Class<?> testClass) {
|
||||
try {
|
||||
resolveContextHierarchyAttributes(testClass);
|
||||
fail("Should throw an IllegalStateException");
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
String msg = String.format(
|
||||
"The @ContextConfiguration elements configured via @ContextHierarchy in test class [%s] must define unique contexts to load.",
|
||||
testClass.getName());
|
||||
assertEquals(msg, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveContextHierarchyAttributesForSingleTestClassWithMultiLevelContextHierarchyWithEmptyContextConfig() {
|
||||
assertContextConfigEntriesAreNotUnique(SingleTestClassWithMultiLevelContextHierarchyWithEmptyContextConfig.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveContextHierarchyAttributesForSingleTestClassWithMultiLevelContextHierarchyWithDuplicatedContextConfig() {
|
||||
assertContextConfigEntriesAreNotUnique(SingleTestClassWithMultiLevelContextHierarchyWithDuplicatedContextConfig.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildContextHierarchyMapForTestClassHierarchyWithMultiLevelContextHierarchies() {
|
||||
Map<String, List<ContextConfigurationAttributes>> map = buildContextHierarchyMap(TestClass3WithMultiLevelContextHierarchy.class);
|
||||
|
||||
assertThat(map.size(), is(3));
|
||||
assertThat(map.keySet(), hasItems("alpha", "beta", "gamma"));
|
||||
|
||||
List<ContextConfigurationAttributes> alphaConfig = map.get("alpha");
|
||||
assertThat(alphaConfig.size(), is(3));
|
||||
assertThat(alphaConfig.get(0).getLocations()[0], is("1-A.xml"));
|
||||
assertThat(alphaConfig.get(1).getLocations()[0], is("2-A.xml"));
|
||||
assertThat(alphaConfig.get(2).getLocations()[0], is("3-A.xml"));
|
||||
|
||||
List<ContextConfigurationAttributes> betaConfig = map.get("beta");
|
||||
assertThat(betaConfig.size(), is(3));
|
||||
assertThat(betaConfig.get(0).getLocations()[0], is("1-B.xml"));
|
||||
assertThat(betaConfig.get(1).getLocations()[0], is("2-B.xml"));
|
||||
assertThat(betaConfig.get(2).getLocations()[0], is("3-B.xml"));
|
||||
|
||||
List<ContextConfigurationAttributes> gammaConfig = map.get("gamma");
|
||||
assertThat(gammaConfig.size(), is(1));
|
||||
assertThat(gammaConfig.get(0).getLocations()[0], is("3-C.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildContextHierarchyMapForTestClassHierarchyWithMultiLevelContextHierarchiesAndUnnamedConfig() {
|
||||
Map<String, List<ContextConfigurationAttributes>> map = buildContextHierarchyMap(TestClass3WithMultiLevelContextHierarchyAndUnnamedConfig.class);
|
||||
|
||||
String level1 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 1;
|
||||
String level2 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 2;
|
||||
String level3 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 3;
|
||||
String level4 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 4;
|
||||
String level5 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 5;
|
||||
String level6 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 6;
|
||||
String level7 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 7;
|
||||
|
||||
assertThat(map.size(), is(7));
|
||||
assertThat(map.keySet(), hasItems(level1, level2, level3, level4, level5, level6, level7));
|
||||
|
||||
List<ContextConfigurationAttributes> level1Config = map.get(level1);
|
||||
assertThat(level1Config.size(), is(1));
|
||||
assertThat(level1Config.get(0).getLocations()[0], is("1-A.xml"));
|
||||
|
||||
List<ContextConfigurationAttributes> level2Config = map.get(level2);
|
||||
assertThat(level2Config.size(), is(1));
|
||||
assertThat(level2Config.get(0).getLocations()[0], is("1-B.xml"));
|
||||
|
||||
List<ContextConfigurationAttributes> level3Config = map.get(level3);
|
||||
assertThat(level3Config.size(), is(1));
|
||||
assertThat(level3Config.get(0).getLocations()[0], is("2-A.xml"));
|
||||
|
||||
// ...
|
||||
|
||||
List<ContextConfigurationAttributes> level7Config = map.get(level7);
|
||||
assertThat(level7Config.size(), is(1));
|
||||
assertThat(level7Config.get(0).getLocations()[0], is("3-C.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildContextHierarchyMapForTestClassHierarchyWithMultiLevelContextHierarchiesAndPartiallyNamedConfig() {
|
||||
Map<String, List<ContextConfigurationAttributes>> map = buildContextHierarchyMap(TestClass2WithMultiLevelContextHierarchyAndPartiallyNamedConfig.class);
|
||||
|
||||
String level1 = "parent";
|
||||
String level2 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 2;
|
||||
String level3 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 3;
|
||||
|
||||
assertThat(map.size(), is(3));
|
||||
assertThat(map.keySet(), hasItems(level1, level2, level3));
|
||||
Iterator<String> levels = map.keySet().iterator();
|
||||
assertThat(levels.next(), is(level1));
|
||||
assertThat(levels.next(), is(level2));
|
||||
assertThat(levels.next(), is(level3));
|
||||
|
||||
List<ContextConfigurationAttributes> level1Config = map.get(level1);
|
||||
assertThat(level1Config.size(), is(2));
|
||||
assertThat(level1Config.get(0).getLocations()[0], is("1-A.xml"));
|
||||
assertThat(level1Config.get(1).getLocations()[0], is("2-A.xml"));
|
||||
|
||||
List<ContextConfigurationAttributes> level2Config = map.get(level2);
|
||||
assertThat(level2Config.size(), is(1));
|
||||
assertThat(level2Config.get(0).getLocations()[0], is("1-B.xml"));
|
||||
|
||||
List<ContextConfigurationAttributes> level3Config = map.get(level3);
|
||||
assertThat(level3Config.size(), is(1));
|
||||
assertThat(level3Config.get(0).getLocations()[0], is("2-C.xml"));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void resolveConfigAttributesWithConflictingLocations() {
|
||||
resolveContextConfigurationAttributes(ConflictingLocations.class);
|
||||
|
|
@ -155,13 +385,13 @@ public class ContextLoaderUtilsTests {
|
|||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void buildMergedConfigWithoutAnnotation() {
|
||||
buildMergedContextConfiguration(Enigma.class, null);
|
||||
buildMergedContextConfiguration(Enigma.class, null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildMergedConfigWithBareAnnotations() {
|
||||
Class<BareAnnotations> testClass = BareAnnotations.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
|
||||
assertMergedConfig(
|
||||
mergedConfig,
|
||||
|
|
@ -173,7 +403,7 @@ public class ContextLoaderUtilsTests {
|
|||
@Test
|
||||
public void buildMergedConfigWithLocalAnnotationAndLocations() {
|
||||
Class<?> testClass = LocationsFoo.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, new String[] { "classpath:/foo.xml" }, EMPTY_CLASS_ARRAY,
|
||||
DelegatingSmartContextLoader.class);
|
||||
|
|
@ -182,7 +412,7 @@ public class ContextLoaderUtilsTests {
|
|||
@Test
|
||||
public void buildMergedConfigWithLocalAnnotationAndClasses() {
|
||||
Class<?> testClass = ClassesFoo.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, new Class<?>[] { FooConfig.class },
|
||||
DelegatingSmartContextLoader.class);
|
||||
|
|
@ -193,7 +423,7 @@ public class ContextLoaderUtilsTests {
|
|||
Class<?> testClass = LocationsFoo.class;
|
||||
Class<? extends ContextLoader> expectedContextLoaderClass = GenericPropertiesContextLoader.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass,
|
||||
expectedContextLoaderClass.getName());
|
||||
expectedContextLoaderClass.getName(), null);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, new String[] { "classpath:/foo.xml" }, EMPTY_CLASS_ARRAY,
|
||||
expectedContextLoaderClass);
|
||||
|
|
@ -204,7 +434,7 @@ public class ContextLoaderUtilsTests {
|
|||
Class<?> testClass = ClassesFoo.class;
|
||||
Class<? extends ContextLoader> expectedContextLoaderClass = GenericPropertiesContextLoader.class;
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass,
|
||||
expectedContextLoaderClass.getName());
|
||||
expectedContextLoaderClass.getName(), null);
|
||||
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, new Class<?>[] { FooConfig.class },
|
||||
expectedContextLoaderClass);
|
||||
|
|
@ -215,7 +445,7 @@ public class ContextLoaderUtilsTests {
|
|||
Class<?> testClass = LocationsBar.class;
|
||||
String[] expectedLocations = new String[] { "/foo.xml", "/bar.xml" };
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
assertMergedConfig(mergedConfig, testClass, expectedLocations, EMPTY_CLASS_ARRAY,
|
||||
AnnotationConfigContextLoader.class);
|
||||
}
|
||||
|
|
@ -225,7 +455,7 @@ public class ContextLoaderUtilsTests {
|
|||
Class<?> testClass = ClassesBar.class;
|
||||
Class<?>[] expectedClasses = new Class<?>[] { FooConfig.class, BarConfig.class };
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses,
|
||||
AnnotationConfigContextLoader.class);
|
||||
}
|
||||
|
|
@ -235,7 +465,7 @@ public class ContextLoaderUtilsTests {
|
|||
Class<?> testClass = OverriddenLocationsBar.class;
|
||||
String[] expectedLocations = new String[] { "/bar.xml" };
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
assertMergedConfig(mergedConfig, testClass, expectedLocations, EMPTY_CLASS_ARRAY,
|
||||
AnnotationConfigContextLoader.class);
|
||||
}
|
||||
|
|
@ -245,7 +475,7 @@ public class ContextLoaderUtilsTests {
|
|||
Class<?> testClass = OverriddenClassesBar.class;
|
||||
Class<?>[] expectedClasses = new Class<?>[] { BarConfig.class };
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses,
|
||||
AnnotationConfigContextLoader.class);
|
||||
}
|
||||
|
|
@ -258,7 +488,7 @@ public class ContextLoaderUtilsTests {
|
|||
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
expectedInitializerClasses.add(FooInitializer.class);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
|
||||
DelegatingSmartContextLoader.class);
|
||||
}
|
||||
|
|
@ -272,7 +502,7 @@ public class ContextLoaderUtilsTests {
|
|||
expectedInitializerClasses.add(FooInitializer.class);
|
||||
expectedInitializerClasses.add(BarInitializer.class);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
|
||||
DelegatingSmartContextLoader.class);
|
||||
}
|
||||
|
|
@ -285,7 +515,7 @@ public class ContextLoaderUtilsTests {
|
|||
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
expectedInitializerClasses.add(BarInitializer.class);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
|
||||
DelegatingSmartContextLoader.class);
|
||||
}
|
||||
|
|
@ -298,7 +528,7 @@ public class ContextLoaderUtilsTests {
|
|||
= new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
expectedInitializerClasses.add(BarInitializer.class);
|
||||
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null);
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
|
||||
assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
|
||||
DelegatingSmartContextLoader.class);
|
||||
}
|
||||
|
|
@ -377,6 +607,8 @@ public class ContextLoaderUtilsTests {
|
|||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private static class Enigma {
|
||||
}
|
||||
|
||||
|
|
@ -475,4 +707,140 @@ public class ContextLoaderUtilsTests {
|
|||
private static class OverriddenInitializersAndClassesBar extends InitializersFoo {
|
||||
}
|
||||
|
||||
@ContextConfiguration("foo.xml")
|
||||
@ContextHierarchy(@ContextConfiguration("bar.xml"))
|
||||
private static class SingleTestClassWithContextConfigurationAndContextHierarchy {
|
||||
}
|
||||
|
||||
@ContextHierarchy(@ContextConfiguration("A.xml"))
|
||||
private static class SingleTestClassWithSingleLevelContextHierarchy {
|
||||
}
|
||||
|
||||
@ContextHierarchy({//
|
||||
//
|
||||
@ContextConfiguration("A.xml"),//
|
||||
@ContextConfiguration("B.xml"),//
|
||||
@ContextConfiguration("C.xml") //
|
||||
})
|
||||
private static class SingleTestClassWithTripleLevelContextHierarchy {
|
||||
}
|
||||
|
||||
@ContextHierarchy(@ContextConfiguration("one.xml"))
|
||||
private static class TestClass1WithSingleLevelContextHierarchy {
|
||||
}
|
||||
|
||||
@ContextHierarchy(@ContextConfiguration({ "two-A.xml", "two-B.xml" }))
|
||||
private static class TestClass2WithSingleLevelContextHierarchy extends TestClass1WithSingleLevelContextHierarchy {
|
||||
}
|
||||
|
||||
@ContextHierarchy(@ContextConfiguration("three.xml"))
|
||||
private static class TestClass3WithSingleLevelContextHierarchy extends TestClass2WithSingleLevelContextHierarchy {
|
||||
}
|
||||
|
||||
@ContextConfiguration("one.xml")
|
||||
private static class TestClass1WithBareContextConfigurationInSuperclass {
|
||||
}
|
||||
|
||||
@ContextHierarchy(@ContextConfiguration("two.xml"))
|
||||
private static class TestClass2WithBareContextConfigurationInSuperclass extends
|
||||
TestClass1WithBareContextConfigurationInSuperclass {
|
||||
}
|
||||
|
||||
@ContextHierarchy(@ContextConfiguration("one.xml"))
|
||||
private static class TestClass1WithBareContextConfigurationInSubclass {
|
||||
}
|
||||
|
||||
@ContextConfiguration("two.xml")
|
||||
private static class TestClass2WithBareContextConfigurationInSubclass extends
|
||||
TestClass1WithBareContextConfigurationInSuperclass {
|
||||
}
|
||||
|
||||
@ContextHierarchy({//
|
||||
//
|
||||
@ContextConfiguration(locations = "1-A.xml", name = "alpha"),//
|
||||
@ContextConfiguration(locations = "1-B.xml", name = "beta") //
|
||||
})
|
||||
private static class TestClass1WithMultiLevelContextHierarchy {
|
||||
}
|
||||
|
||||
@ContextHierarchy({//
|
||||
//
|
||||
@ContextConfiguration(locations = "2-A.xml", name = "alpha"),//
|
||||
@ContextConfiguration(locations = "2-B.xml", name = "beta") //
|
||||
})
|
||||
private static class TestClass2WithMultiLevelContextHierarchy extends TestClass1WithMultiLevelContextHierarchy {
|
||||
}
|
||||
|
||||
@ContextHierarchy({//
|
||||
//
|
||||
@ContextConfiguration(locations = "3-A.xml", name = "alpha"),//
|
||||
@ContextConfiguration(locations = "3-B.xml", name = "beta"),//
|
||||
@ContextConfiguration(locations = "3-C.xml", name = "gamma") //
|
||||
})
|
||||
private static class TestClass3WithMultiLevelContextHierarchy extends TestClass2WithMultiLevelContextHierarchy {
|
||||
}
|
||||
|
||||
@ContextHierarchy({//
|
||||
//
|
||||
@ContextConfiguration(locations = "1-A.xml"),//
|
||||
@ContextConfiguration(locations = "1-B.xml") //
|
||||
})
|
||||
private static class TestClass1WithMultiLevelContextHierarchyAndUnnamedConfig {
|
||||
}
|
||||
|
||||
@ContextHierarchy({//
|
||||
//
|
||||
@ContextConfiguration(locations = "2-A.xml"),//
|
||||
@ContextConfiguration(locations = "2-B.xml") //
|
||||
})
|
||||
private static class TestClass2WithMultiLevelContextHierarchyAndUnnamedConfig extends
|
||||
TestClass1WithMultiLevelContextHierarchyAndUnnamedConfig {
|
||||
}
|
||||
|
||||
@ContextHierarchy({//
|
||||
//
|
||||
@ContextConfiguration(locations = "3-A.xml"),//
|
||||
@ContextConfiguration(locations = "3-B.xml"),//
|
||||
@ContextConfiguration(locations = "3-C.xml") //
|
||||
})
|
||||
private static class TestClass3WithMultiLevelContextHierarchyAndUnnamedConfig extends
|
||||
TestClass2WithMultiLevelContextHierarchyAndUnnamedConfig {
|
||||
}
|
||||
|
||||
@ContextHierarchy({//
|
||||
//
|
||||
@ContextConfiguration(locations = "1-A.xml", name = "parent"),//
|
||||
@ContextConfiguration(locations = "1-B.xml") //
|
||||
})
|
||||
private static class TestClass1WithMultiLevelContextHierarchyAndPartiallyNamedConfig {
|
||||
}
|
||||
|
||||
@ContextHierarchy({//
|
||||
//
|
||||
@ContextConfiguration(locations = "2-A.xml", name = "parent"),//
|
||||
@ContextConfiguration(locations = "2-C.xml") //
|
||||
})
|
||||
private static class TestClass2WithMultiLevelContextHierarchyAndPartiallyNamedConfig extends
|
||||
TestClass1WithMultiLevelContextHierarchyAndPartiallyNamedConfig {
|
||||
}
|
||||
|
||||
@ContextHierarchy({
|
||||
//
|
||||
@ContextConfiguration,//
|
||||
@ContextConfiguration //
|
||||
})
|
||||
private static class SingleTestClassWithMultiLevelContextHierarchyWithEmptyContextConfig {
|
||||
}
|
||||
|
||||
@ContextHierarchy({
|
||||
//
|
||||
@ContextConfiguration("foo.xml"),//
|
||||
@ContextConfiguration(classes = BarConfig.class),// duplicate!
|
||||
@ContextConfiguration("baz.xml"),//
|
||||
@ContextConfiguration(classes = BarConfig.class),// duplicate!
|
||||
@ContextConfiguration(loader = AnnotationConfigContextLoader.class) //
|
||||
})
|
||||
private static class SingleTestClassWithMultiLevelContextHierarchyWithDuplicatedContextConfig {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -76,7 +76,7 @@ public class MergedContextConfigurationTests {
|
|||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, new AnnotationConfigContextLoader());
|
||||
assertFalse(mergedConfig1.hashCode() == mergedConfig2.hashCode());
|
||||
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -97,7 +97,7 @@ public class MergedContextConfigurationTests {
|
|||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), locations2,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
assertFalse(mergedConfig1.hashCode() == mergedConfig2.hashCode());
|
||||
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -118,7 +118,7 @@ public class MergedContextConfigurationTests {
|
|||
classes1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
classes2, EMPTY_STRING_ARRAY, loader);
|
||||
assertFalse(mergedConfig1.hashCode() == mergedConfig2.hashCode());
|
||||
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -161,7 +161,7 @@ public class MergedContextConfigurationTests {
|
|||
EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
assertFalse(mergedConfig1.hashCode() == mergedConfig2.hashCode());
|
||||
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -197,15 +197,47 @@ public class MergedContextConfigurationTests {
|
|||
EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
|
||||
assertFalse(mergedConfig1.hashCode() == mergedConfig2.hashCode());
|
||||
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@Test
|
||||
public void hashCodeWithSameParent() {
|
||||
MergedContextConfiguration parent = new MergedContextConfiguration(getClass(), new String[] { "foo", "bar}" },
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent);
|
||||
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@Test
|
||||
public void hashCodeWithDifferentParents() {
|
||||
MergedContextConfiguration parent1 = new MergedContextConfiguration(getClass(), new String[] { "foo", "bar}" },
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration parent2 = new MergedContextConfiguration(getClass(), new String[] { "baz", "quux" },
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent1);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent2);
|
||||
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsBasics() {
|
||||
MergedContextConfiguration mergedConfig = new MergedContextConfiguration(null, null, null, null, null);
|
||||
assertTrue(mergedConfig.equals(mergedConfig));
|
||||
assertFalse(mergedConfig.equals(null));
|
||||
assertFalse(mergedConfig.equals(new Integer(1)));
|
||||
assertEquals(mergedConfig, mergedConfig);
|
||||
assertNotEquals(mergedConfig, null);
|
||||
assertNotEquals(mergedConfig, new Integer(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -237,8 +269,8 @@ public class MergedContextConfigurationTests {
|
|||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, new AnnotationConfigContextLoader());
|
||||
assertFalse(mergedConfig1.equals(mergedConfig2));
|
||||
assertFalse(mergedConfig2.equals(mergedConfig1));
|
||||
assertNotEquals(mergedConfig1, mergedConfig2);
|
||||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -259,8 +291,8 @@ public class MergedContextConfigurationTests {
|
|||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), locations2,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
assertFalse(mergedConfig1.equals(mergedConfig2));
|
||||
assertFalse(mergedConfig2.equals(mergedConfig1));
|
||||
assertNotEquals(mergedConfig1, mergedConfig2);
|
||||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -281,8 +313,8 @@ public class MergedContextConfigurationTests {
|
|||
classes1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
classes2, EMPTY_STRING_ARRAY, loader);
|
||||
assertFalse(mergedConfig1.equals(mergedConfig2));
|
||||
assertFalse(mergedConfig2.equals(mergedConfig1));
|
||||
assertNotEquals(mergedConfig1, mergedConfig2);
|
||||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -325,8 +357,8 @@ public class MergedContextConfigurationTests {
|
|||
EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
assertFalse(mergedConfig1.equals(mergedConfig2));
|
||||
assertFalse(mergedConfig2.equals(mergedConfig1));
|
||||
assertNotEquals(mergedConfig1, mergedConfig2);
|
||||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -362,8 +394,42 @@ public class MergedContextConfigurationTests {
|
|||
EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
|
||||
assertFalse(mergedConfig1.equals(mergedConfig2));
|
||||
assertFalse(mergedConfig2.equals(mergedConfig1));
|
||||
assertNotEquals(mergedConfig1, mergedConfig2);
|
||||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@Test
|
||||
public void equalsWithSameParent() {
|
||||
MergedContextConfiguration parent = new MergedContextConfiguration(getClass(), new String[] { "foo", "bar}" },
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent);
|
||||
assertEquals(mergedConfig1, mergedConfig2);
|
||||
assertEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@Test
|
||||
public void equalsWithDifferentParents() {
|
||||
MergedContextConfiguration parent1 = new MergedContextConfiguration(getClass(), new String[] { "foo", "bar}" },
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration parent2 = new MergedContextConfiguration(getClass(), new String[] { "baz", "quux" },
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent1);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent2);
|
||||
assertNotEquals(mergedConfig1, mergedConfig2);
|
||||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ import org.springframework.test.context.support.DirtiesContextTestExecutionListe
|
|||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see TestContextCacheKeyTests
|
||||
* @see ContextCacheTests
|
||||
*/
|
||||
@RunWith(OrderedMethodsSpringJUnit4ClassRunner.class)
|
||||
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2012 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 static org.junit.Assert.assertNotNull;
|
||||
import static org.springframework.test.context.SpringRunnerContextCacheTests.assertContextCacheStatistics;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
|
||||
/**
|
||||
* Unit tests for verifying proper behavior of the {@link ContextCache} in
|
||||
* conjunction with cache keys used in {@link TestContext}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
* @see SpringRunnerContextCacheTests
|
||||
*/
|
||||
public class TestContextCacheKeyTests {
|
||||
|
||||
private ContextCache contextCache = new ContextCache();
|
||||
|
||||
|
||||
@Before
|
||||
public void initialCacheState() {
|
||||
assertContextCacheStatistics(contextCache, "initial state", 0, 0, 0);
|
||||
}
|
||||
|
||||
private void loadAppCtxAndAssertCacheStats(Class<?> testClass, int expectedSize, int expectedHitCount,
|
||||
int expectedMissCount) {
|
||||
TestContext testContext = new TestContext(testClass, contextCache);
|
||||
ApplicationContext context = testContext.getApplicationContext();
|
||||
assertNotNull(context);
|
||||
assertContextCacheStatistics(contextCache, testClass.getName(), expectedSize, expectedHitCount,
|
||||
expectedMissCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyCacheKeyIsBasedOnContextLoader() {
|
||||
loadAppCtxAndAssertCacheStats(AnnotationConfigContextLoaderTestCase.class, 1, 0, 1);
|
||||
loadAppCtxAndAssertCacheStats(AnnotationConfigContextLoaderTestCase.class, 1, 1, 1);
|
||||
loadAppCtxAndAssertCacheStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 1, 2);
|
||||
loadAppCtxAndAssertCacheStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 2, 2);
|
||||
loadAppCtxAndAssertCacheStats(AnnotationConfigContextLoaderTestCase.class, 2, 3, 2);
|
||||
loadAppCtxAndAssertCacheStats(CustomAnnotationConfigContextLoaderTestCase.class, 2, 4, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyCacheKeyIsBasedOnActiveProfiles() {
|
||||
loadAppCtxAndAssertCacheStats(FooBarProfilesTestCase.class, 1, 0, 1);
|
||||
loadAppCtxAndAssertCacheStats(FooBarProfilesTestCase.class, 1, 1, 1);
|
||||
// Profiles {foo, bar} should hash to the same as {bar,foo}
|
||||
loadAppCtxAndAssertCacheStats(BarFooProfilesTestCase.class, 1, 2, 1);
|
||||
loadAppCtxAndAssertCacheStats(FooBarProfilesTestCase.class, 1, 3, 1);
|
||||
loadAppCtxAndAssertCacheStats(FooBarProfilesTestCase.class, 1, 4, 1);
|
||||
loadAppCtxAndAssertCacheStats(BarFooProfilesTestCase.class, 1, 5, 1);
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
}
|
||||
|
||||
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
|
||||
private static class AnnotationConfigContextLoaderTestCase {
|
||||
}
|
||||
|
||||
@ContextConfiguration(classes = Config.class, loader = CustomAnnotationConfigContextLoader.class)
|
||||
private static class CustomAnnotationConfigContextLoaderTestCase {
|
||||
}
|
||||
|
||||
private static class CustomAnnotationConfigContextLoader extends AnnotationConfigContextLoader {
|
||||
}
|
||||
|
||||
@ActiveProfiles({ "foo", "bar" })
|
||||
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
|
||||
private static class FooBarProfilesTestCase {
|
||||
}
|
||||
|
||||
@ActiveProfiles({ "bar", "foo" })
|
||||
@ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
|
||||
private static class BarFooProfilesTestCase {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy({
|
||||
//
|
||||
@ContextConfiguration(name = "parent", classes = ClassHierarchyWithMergedConfigLevelOneTests.AppConfig.class),//
|
||||
@ContextConfiguration(name = "child", classes = ClassHierarchyWithMergedConfigLevelOneTests.UserConfig.class) //
|
||||
})
|
||||
public class ClassHierarchyWithMergedConfigLevelOneTests {
|
||||
|
||||
@Configuration
|
||||
static class AppConfig {
|
||||
|
||||
@Bean
|
||||
public String parent() {
|
||||
return "parent";
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class UserConfig {
|
||||
|
||||
@Autowired
|
||||
private AppConfig appConfig;
|
||||
|
||||
|
||||
@Bean
|
||||
public String user() {
|
||||
return appConfig.parent() + " + user";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String beanFromUserConfig() {
|
||||
return "from UserConfig";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
protected String parent;
|
||||
|
||||
@Autowired
|
||||
protected String user;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("beanFromUserConfig")
|
||||
protected String beanFromUserConfig;
|
||||
|
||||
@Autowired
|
||||
protected ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
public void loadContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNotNull("parent ApplicationContext", context.getParent());
|
||||
assertNull("grandparent ApplicationContext", context.getParent().getParent());
|
||||
assertEquals("parent", parent);
|
||||
assertEquals("parent + user", user);
|
||||
assertEquals("from UserConfig", beanFromUserConfig);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy(@ContextConfiguration(name = "child", classes = ClassHierarchyWithMergedConfigLevelTwoTests.OrderConfig.class))
|
||||
public class ClassHierarchyWithMergedConfigLevelTwoTests extends ClassHierarchyWithMergedConfigLevelOneTests {
|
||||
|
||||
@Configuration
|
||||
static class OrderConfig {
|
||||
|
||||
@Autowired
|
||||
private ClassHierarchyWithMergedConfigLevelOneTests.UserConfig userConfig;
|
||||
|
||||
@Bean
|
||||
public String order() {
|
||||
return userConfig.user() + " + order";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private String order;
|
||||
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void loadContextHierarchy() {
|
||||
super.loadContextHierarchy();
|
||||
assertEquals("parent + user + order", order);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy(@ContextConfiguration(name = "child", classes = ClassHierarchyWithOverriddenConfigLevelTwoTests.TestUserConfig.class, inheritLocations = false))
|
||||
public class ClassHierarchyWithOverriddenConfigLevelTwoTests extends ClassHierarchyWithMergedConfigLevelOneTests {
|
||||
|
||||
@Configuration
|
||||
static class TestUserConfig {
|
||||
|
||||
@Autowired
|
||||
private ClassHierarchyWithMergedConfigLevelOneTests.AppConfig appConfig;
|
||||
|
||||
|
||||
@Bean
|
||||
public String user() {
|
||||
return appConfig.parent() + " + test user";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String beanFromTestUserConfig() {
|
||||
return "from TestUserConfig";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private String beanFromTestUserConfig;
|
||||
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void loadContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNotNull("parent ApplicationContext", context.getParent());
|
||||
assertNull("grandparent ApplicationContext", context.getParent().getParent());
|
||||
assertEquals("parent", parent);
|
||||
assertEquals("parent + test user", user);
|
||||
assertEquals("from TestUserConfig", beanFromTestUserConfig);
|
||||
assertNull("Bean from UserConfig should not be present.", beanFromUserConfig);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Integration tests that verify support for {@link DirtiesContext.HierarchyMode}
|
||||
* in conjunction with context hierarchies configured via {@link ContextHierarchy}.
|
||||
*
|
||||
* <p>Note that correct method execution order is essential, thus the use of
|
||||
* {@link FixMethodOrder}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy({ @ContextConfiguration(classes = DirtiesContextWithContextHierarchyTests.ParentConfig.class),
|
||||
@ContextConfiguration(classes = DirtiesContextWithContextHierarchyTests.ChildConfig.class) })
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class DirtiesContextWithContextHierarchyTests {
|
||||
|
||||
@Configuration
|
||||
static class ParentConfig {
|
||||
|
||||
@Bean
|
||||
public StringBuffer foo() {
|
||||
return new StringBuffer("foo");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StringBuffer baz() {
|
||||
return new StringBuffer("baz-parent");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ChildConfig {
|
||||
|
||||
@Bean
|
||||
public StringBuffer baz() {
|
||||
return new StringBuffer("baz-child");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private StringBuffer foo;
|
||||
|
||||
@Autowired
|
||||
private StringBuffer baz;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void reverseStringBuffers() {
|
||||
foo.reverse();
|
||||
baz.reverse();
|
||||
}
|
||||
|
||||
private void assertOriginalState() {
|
||||
assertCleanParentContext();
|
||||
assertCleanChildContext();
|
||||
}
|
||||
|
||||
private void assertCleanParentContext() {
|
||||
assertEquals("foo", foo.toString());
|
||||
}
|
||||
|
||||
private void assertCleanChildContext() {
|
||||
assertEquals("baz-child", baz.toString());
|
||||
}
|
||||
|
||||
private void assertDirtyParentContext() {
|
||||
assertEquals("oof", foo.toString());
|
||||
}
|
||||
|
||||
private void assertDirtyChildContext() {
|
||||
assertEquals("dlihc-zab", baz.toString());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Before
|
||||
public void verifyContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNotNull("parent ApplicationContext", context.getParent());
|
||||
assertNull("grandparent ApplicationContext", context.getParent().getParent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1_verifyOriginalStateAndDirtyContexts() {
|
||||
assertOriginalState();
|
||||
reverseStringBuffers();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void test2_verifyContextsWereDirtiedAndTriggerExhaustiveCacheClearing() {
|
||||
assertDirtyParentContext();
|
||||
assertDirtyChildContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext(hierarchyMode = HierarchyMode.CURRENT_LEVEL)
|
||||
public void test3_verifyOriginalStateWasReinstatedAndDirtyContextsAndTriggerCurrentLevelCacheClearing() {
|
||||
assertOriginalState();
|
||||
reverseStringBuffers();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test4_verifyParentContextIsStillDirtyButChildContextHasBeenReinstated() {
|
||||
assertDirtyParentContext();
|
||||
assertCleanChildContext();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy(@ContextConfiguration)
|
||||
public class SingleTestClassWithSingleLevelContextHierarchyTests {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
public void loadContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNull("parent ApplicationContext", context.getParent());
|
||||
assertEquals("foo", foo);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy({
|
||||
@ContextConfiguration(classes = SingleTestClassWithTwoLevelContextHierarchyAndMixedConfigTypesTests.ParentConfig.class),
|
||||
@ContextConfiguration("SingleTestClassWithTwoLevelContextHierarchyAndMixedConfigTypesTests-ChildConfig.xml") })
|
||||
public class SingleTestClassWithTwoLevelContextHierarchyAndMixedConfigTypesTests {
|
||||
|
||||
@Configuration
|
||||
static class ParentConfig {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String baz() {
|
||||
return "baz-parent";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Autowired
|
||||
private String bar;
|
||||
|
||||
@Autowired
|
||||
private String baz;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
public void loadContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNotNull("parent ApplicationContext", context.getParent());
|
||||
assertNull("grandparent ApplicationContext", context.getParent().getParent());
|
||||
assertEquals("foo", foo);
|
||||
assertEquals("bar", bar);
|
||||
assertEquals("baz-child", baz);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy({
|
||||
@ContextConfiguration(classes = SingleTestClassWithTwoLevelContextHierarchyTests.ParentConfig.class),
|
||||
@ContextConfiguration(classes = SingleTestClassWithTwoLevelContextHierarchyTests.ChildConfig.class) })
|
||||
public class SingleTestClassWithTwoLevelContextHierarchyTests {
|
||||
|
||||
@Configuration
|
||||
static class ParentConfig {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String baz() {
|
||||
return "baz-parent";
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ChildConfig {
|
||||
|
||||
@Bean
|
||||
public String bar() {
|
||||
return "bar";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String baz() {
|
||||
return "baz-child";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Autowired
|
||||
private String bar;
|
||||
|
||||
@Autowired
|
||||
private String baz;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
public void loadContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNotNull("parent ApplicationContext", context.getParent());
|
||||
assertNull("grandparent ApplicationContext", context.getParent().getParent());
|
||||
assertEquals("foo", foo);
|
||||
assertEquals("bar", bar);
|
||||
assertEquals("baz-child", baz);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy(@ContextConfiguration)
|
||||
public class TestHierarchyLevelOneWithBareContextConfigurationInSubclassTests {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo-level-1";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String bar() {
|
||||
return "bar";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Autowired
|
||||
private String bar;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
public void loadContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNull("parent ApplicationContext", context.getParent());
|
||||
assertEquals("foo-level-1", foo);
|
||||
assertEquals("bar", bar);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class TestHierarchyLevelOneWithBareContextConfigurationInSuperclassTests {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo-level-1";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String bar() {
|
||||
return "bar";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Autowired
|
||||
private String bar;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
public void loadContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNull("parent ApplicationContext", context.getParent());
|
||||
assertEquals("foo-level-1", foo);
|
||||
assertEquals("bar", bar);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy(@ContextConfiguration)
|
||||
public class TestHierarchyLevelOneWithSingleLevelContextHierarchyTests {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo-level-1";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String bar() {
|
||||
return "bar";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Autowired
|
||||
private String bar;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
public void loadContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNull("parent ApplicationContext", context.getParent());
|
||||
assertEquals("foo-level-1", foo);
|
||||
assertEquals("bar", bar);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class TestHierarchyLevelTwoWithBareContextConfigurationInSubclassTests extends
|
||||
TestHierarchyLevelOneWithBareContextConfigurationInSubclassTests {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo-level-2";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String baz() {
|
||||
return "baz";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Autowired
|
||||
private String bar;
|
||||
|
||||
@Autowired
|
||||
private String baz;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void loadContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNotNull("parent ApplicationContext", context.getParent());
|
||||
assertNull("grandparent ApplicationContext", context.getParent().getParent());
|
||||
assertEquals("foo-level-2", foo);
|
||||
assertEquals("bar", bar);
|
||||
assertEquals("baz", baz);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy(@ContextConfiguration)
|
||||
public class TestHierarchyLevelTwoWithBareContextConfigurationInSuperclassTests extends
|
||||
TestHierarchyLevelOneWithBareContextConfigurationInSuperclassTests {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo-level-2";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String baz() {
|
||||
return "baz";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Autowired
|
||||
private String bar;
|
||||
|
||||
@Autowired
|
||||
private String baz;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void loadContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNotNull("parent ApplicationContext", context.getParent());
|
||||
assertNull("grandparent ApplicationContext", context.getParent().getParent());
|
||||
assertEquals("foo-level-2", foo);
|
||||
assertEquals("bar", bar);
|
||||
assertEquals("baz", baz);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy(@ContextConfiguration)
|
||||
public class TestHierarchyLevelTwoWithSingleLevelContextHierarchyAndMixedConfigTypesTests extends
|
||||
TestHierarchyLevelOneWithSingleLevelContextHierarchyTests {
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Autowired
|
||||
private String bar;
|
||||
|
||||
@Autowired
|
||||
private String baz;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void loadContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNotNull("parent ApplicationContext", context.getParent());
|
||||
assertNull("grandparent ApplicationContext", context.getParent().getParent());
|
||||
assertEquals("foo-level-2", foo);
|
||||
assertEquals("bar", bar);
|
||||
assertEquals("baz", baz);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.standard;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextHierarchy(@ContextConfiguration)
|
||||
public class TestHierarchyLevelTwoWithSingleLevelContextHierarchyTests extends
|
||||
TestHierarchyLevelOneWithSingleLevelContextHierarchyTests {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo-level-2";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String baz() {
|
||||
return "baz";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Autowired
|
||||
private String bar;
|
||||
|
||||
@Autowired
|
||||
private String baz;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void loadContextHierarchy() {
|
||||
assertNotNull("child ApplicationContext", context);
|
||||
assertNotNull("parent ApplicationContext", context.getParent());
|
||||
assertEquals("foo-level-2", foo);
|
||||
assertEquals("bar", bar);
|
||||
assertEquals("baz", baz);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.web;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.hierarchies.web.ControllerIntegrationTests.AppConfig;
|
||||
import org.springframework.test.context.hierarchies.web.ControllerIntegrationTests.WebConfig;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@WebAppConfiguration
|
||||
@ContextHierarchy({
|
||||
//
|
||||
@ContextConfiguration(name = "root", classes = AppConfig.class),
|
||||
@ContextConfiguration(name = "dispatcher", classes = WebConfig.class) //
|
||||
})
|
||||
public class ControllerIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
static class AppConfig {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WebConfig {
|
||||
|
||||
@Bean
|
||||
public String bar() {
|
||||
return "bar";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext wac;
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Autowired
|
||||
private String bar;
|
||||
|
||||
|
||||
@Test
|
||||
public void verifyRootWacSupport() {
|
||||
assertEquals("foo", foo);
|
||||
assertEquals("bar", bar);
|
||||
|
||||
ApplicationContext parent = wac.getParent();
|
||||
assertNotNull(parent);
|
||||
assertTrue(parent instanceof WebApplicationContext);
|
||||
WebApplicationContext root = (WebApplicationContext) parent;
|
||||
assertFalse(root.getBeansOfType(String.class).containsKey("bar"));
|
||||
|
||||
ServletContext childServletContext = wac.getServletContext();
|
||||
assertNotNull(childServletContext);
|
||||
ServletContext rootServletContext = root.getServletContext();
|
||||
assertNotNull(rootServletContext);
|
||||
assertSame(childServletContext, rootServletContext);
|
||||
|
||||
assertSame(root, rootServletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
|
||||
assertSame(root, childServletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.web;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@ContextHierarchy(@ContextConfiguration)
|
||||
public class DispatcherWacRootWacEarTests extends RootWacEarTests {
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext wac;
|
||||
|
||||
@Autowired
|
||||
private String ear;
|
||||
|
||||
@Autowired
|
||||
private String root;
|
||||
|
||||
@Autowired
|
||||
private String dispatcher;
|
||||
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void verifyEarConfig() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void verifyRootWacConfig() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyDispatcherWacConfig() {
|
||||
ApplicationContext parent = wac.getParent();
|
||||
assertNotNull(parent);
|
||||
assertTrue(parent instanceof WebApplicationContext);
|
||||
|
||||
ApplicationContext grandParent = parent.getParent();
|
||||
assertNotNull(grandParent);
|
||||
assertFalse(grandParent instanceof WebApplicationContext);
|
||||
|
||||
ServletContext dispatcherServletContext = wac.getServletContext();
|
||||
assertNotNull(dispatcherServletContext);
|
||||
ServletContext rootServletContext = ((WebApplicationContext) parent).getServletContext();
|
||||
assertNotNull(rootServletContext);
|
||||
assertSame(dispatcherServletContext, rootServletContext);
|
||||
|
||||
assertSame(parent,
|
||||
rootServletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
|
||||
assertSame(parent,
|
||||
dispatcherServletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
|
||||
|
||||
assertEquals("ear", ear);
|
||||
assertEquals("root", root);
|
||||
assertEquals("dispatcher", dispatcher);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.web;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class EarTests {
|
||||
|
||||
@Configuration
|
||||
static class EarConfig {
|
||||
|
||||
@Bean
|
||||
public String ear() {
|
||||
return "ear";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private String ear;
|
||||
|
||||
|
||||
@Test
|
||||
public void verifyEarConfig() {
|
||||
assertFalse(context instanceof WebApplicationContext);
|
||||
assertNull(context.getParent());
|
||||
assertEquals("ear", ear);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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.hierarchies.web;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2.2
|
||||
*/
|
||||
@WebAppConfiguration
|
||||
@ContextHierarchy(@ContextConfiguration)
|
||||
public class RootWacEarTests extends EarTests {
|
||||
|
||||
@Configuration
|
||||
static class RootWacConfig {
|
||||
|
||||
@Bean
|
||||
public String root() {
|
||||
return "root";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext wac;
|
||||
|
||||
@Autowired
|
||||
private String ear;
|
||||
|
||||
@Autowired
|
||||
private String root;
|
||||
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void verifyEarConfig() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyRootWacConfig() {
|
||||
ApplicationContext parent = wac.getParent();
|
||||
assertNotNull(parent);
|
||||
assertFalse(parent instanceof WebApplicationContext);
|
||||
assertEquals("ear", ear);
|
||||
assertEquals("root", root);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -33,21 +33,21 @@ import org.springframework.test.context.support.GenericPropertiesContextLoader;
|
|||
/**
|
||||
* <p>
|
||||
* JUnit 4 based test class, which verifies the expected functionality of
|
||||
* {@link SpringJUnit4ClassRunner} in conjunction with support for application
|
||||
* contexts loaded from Java {@link Properties} files. Specifically, the
|
||||
* {@link ContextConfiguration#loader() loaderClass} and
|
||||
* {@link ContextConfiguration#resourceSuffix() resourceSuffix} attributes of
|
||||
* @ContextConfiguration are tested.
|
||||
* {@link SpringJUnit4ClassRunner} in conjunction with support for application contexts
|
||||
* loaded from Java {@link Properties} files. Specifically, the
|
||||
* {@link ContextConfiguration#loader() loader} attribute of {@code ContextConfiguration}
|
||||
* and the
|
||||
* {@link org.springframework.test.context.support.GenericPropertiesContextLoader#getResourceSuffix()
|
||||
* resourceSuffix} property of {@code GenericPropertiesContextLoader} are tested.
|
||||
* </p>
|
||||
* <p>
|
||||
* Since no {@link ContextConfiguration#locations() locations} are explicitly
|
||||
* defined, the {@link ContextConfiguration#resourceSuffix() resourceSuffix} is
|
||||
* set to "-context.properties", and
|
||||
* {@link ContextConfiguration#generateDefaultLocations() generateDefaultLocations}
|
||||
* is left set to its default value of {@code true}, this test class's
|
||||
* dependencies will be injected via
|
||||
* {@link Autowired annotation-based autowiring} from beans defined in the
|
||||
* {@link ApplicationContext} loaded from the default classpath resource: "{@code /org/springframework/test/junit4/PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests-context.properties}".
|
||||
* Since no {@link ContextConfiguration#locations() locations} are explicitly defined, the
|
||||
* {@code resourceSuffix} is set to "-context.properties", and since default
|
||||
* resource locations will be detected by default, this test class's dependencies will be
|
||||
* injected via {@link Autowired annotation-based autowiring} from beans defined in the
|
||||
* {@link ApplicationContext} loaded from the default classpath resource: "
|
||||
* {@code /org/springframework/test/junit4/PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests-context.properties}
|
||||
* ".
|
||||
* </p>
|
||||
*
|
||||
* @author Sam Brannen
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import org.junit.runners.Suite.SuiteClasses;
|
|||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({ TestClass1.class, TestClass2.class })
|
||||
public class Spr8849Tests {
|
||||
|
|
|
|||
|
|
@ -43,10 +43,11 @@ import org.testng.annotations.Test;
|
|||
|
||||
/**
|
||||
* Integration tests that verify support for
|
||||
* {@link import org.springframework.context.annotation.Configuration @Configuration}
|
||||
* classes with TestNG-based tests.
|
||||
* {@link org.springframework.context.annotation.Configuration @Configuration} classes
|
||||
* with TestNG-based tests.
|
||||
*
|
||||
* <p>Configuration will be loaded from
|
||||
* <p>
|
||||
* Configuration will be loaded from
|
||||
* {@link AnnotationConfigTransactionalTestNGSpringContextTests.ContextConfiguration}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="bar" class="java.lang.String" c:_="bar" />
|
||||
|
||||
<bean id="baz" class="java.lang.String" c:_="baz-child" />
|
||||
|
||||
</beans>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="foo" class="java.lang.String" c:_="foo-level-2" />
|
||||
|
||||
<bean id="baz" class="java.lang.String" c:_="baz" />
|
||||
|
||||
</beans>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="dispatcher" class="java.lang.String" c:_="dispatcher" />
|
||||
|
||||
</beans>
|
||||
|
|
@ -41,6 +41,8 @@ Changes in version 3.2.2 (2013-03-11)
|
|||
* MappingJackson(2)JsonView allows subclasses to access the ObjectMapper and to override content writing (SPR-7619)
|
||||
* Log4jWebConfigurer supports resolving placeholders against ServletContext init-parameters as well (SPR-10284)
|
||||
* consistent use of LinkedHashMaps and independent getAttributeNames Enumeration in Servlet/Portlet mocks (SPR-10224)
|
||||
* introduced support for context hierarchies in the TestContext framework (SPR-5613)
|
||||
* introduced support for WebApplicationContext hierarchies in the TestContext framework (SPR-9863)
|
||||
|
||||
|
||||
Changes in version 3.2.1 (2013-01-24)
|
||||
|
|
|
|||
Loading…
Reference in New Issue