diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/ContextConfiguration.java b/org.springframework.test/src/main/java/org/springframework/test/context/ContextConfiguration.java index baaa4343437755f413c017859634a7aeca31c36b..6c0f46a095ee9be5a8eb26b4b151c16a8f3ac41c 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/ContextConfiguration.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/ContextConfiguration.java @@ -87,6 +87,10 @@ public @interface ContextConfiguration { /** * The type of {@link ContextLoader} to use for loading an * {@link org.springframework.context.ApplicationContext ApplicationContext}. + *

If not specified, the loader will be inherited from the first superclass + * which is annotated with @ContextConfiguration and specifies + * an explicit loader. If no class in the hierarchy specifies an explicit + * loader, a default loader will be used instead. */ Class loader() default ContextLoader.class; diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/TestContext.java b/org.springframework.test/src/main/java/org/springframework/test/context/TestContext.java index 575d14b4f0f99d9379b59ebcca2daa871959a577..a8e8d7b3216f875a03348d903e104c7b27a22d92 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/TestContext.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/TestContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 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. @@ -95,7 +95,6 @@ public class TestContext extends AttributeAccessorSupport { * @param defaultContextLoaderClassName the name of the default * ContextLoader class to use (may be null) */ - @SuppressWarnings("unchecked") TestContext(Class testClass, ContextCache contextCache, String defaultContextLoaderClassName) { Assert.notNull(testClass, "Test class must not be null"); Assert.notNull(contextCache, "ContextCache must not be null"); @@ -119,23 +118,8 @@ public class TestContext extends AttributeAccessorSupport { + "]"); } - Class contextLoaderClass = contextConfiguration.loader(); - if (ContextLoader.class.equals(contextLoaderClass)) { - try { - if (logger.isTraceEnabled()) { - logger.trace("Using default ContextLoader class [" + defaultContextLoaderClassName - + "] for @ContextConfiguration [" + contextConfiguration + "] and class [" + testClass - + "]"); - } - contextLoaderClass = (Class) getClass().getClassLoader().loadClass( - defaultContextLoaderClassName); - } - catch (ClassNotFoundException ex) { - throw new IllegalStateException("Could not load default ContextLoader class [" - + defaultContextLoaderClassName + "]. Specify @ContextConfiguration's 'loader' " - + "attribute or make the default loader class available."); - } - } + Class contextLoaderClass = retrieveContextLoaderClass(testClass, + defaultContextLoaderClassName); contextLoader = (ContextLoader) BeanUtils.instantiateClass(contextLoaderClass); locations = retrieveContextLocations(contextLoader, testClass); } @@ -146,6 +130,80 @@ public class TestContext extends AttributeAccessorSupport { this.locations = locations; } + /** + *

+ * Retrieve the {@link ContextLoader} {@link Class} to use for the supplied + * {@link Class test class}. + *

    + *
  1. If the {@link ContextConfiguration#loader() loader} attribute of + * {@link ContextConfiguration @ContextConfiguration} is configured + * with an explicit class, that class will be returned.
  2. + *
  3. If a loader class is not specified, the class hierarchy + * will be traversed to find a parent class annotated with + * @ContextConfiguration; go to step #1.
  4. + *
+ *

+ * If no explicit loader class is found after traversing the + * class hierarchy, an attempt will be made to load and return the class + * with the supplied defaultContextLoaderClassName. + * + * @param clazz the class for which to retrieve ContextLoader + * class; must not be null + * @param defaultContextLoaderClassName the name of the default + * ContextLoader class to use; must not be null or + * empty + * @return the ContextLoader class to use for the specified + * class + * @throws IllegalArgumentException if {@link ContextConfiguration + * @ContextConfiguration} is not present on the supplied class + */ + @SuppressWarnings("unchecked") + private Class retrieveContextLoaderClass(Class clazz, + String defaultContextLoaderClassName) { + Assert.notNull(clazz, "Class must not be null"); + Assert.hasText(defaultContextLoaderClassName, "Default ContextLoader class name must not be null or empty"); + + Class annotationType = ContextConfiguration.class; + Class declaringClass = AnnotationUtils.findAnnotationDeclaringClass(annotationType, clazz); + Assert.notNull(declaringClass, "Could not find an 'annotation declaring class' for annotation type [" + + annotationType + "] and class [" + clazz + "]"); + + while (declaringClass != null) { + ContextConfiguration contextConfiguration = declaringClass.getAnnotation(annotationType); + if (logger.isTraceEnabled()) { + logger.trace("Processing ContextLoader for @ContextConfiguration [" + contextConfiguration + + "] and declaring class [" + declaringClass + "]"); + } + + Class contextLoaderClass = contextConfiguration.loader(); + if (!ContextLoader.class.equals(contextLoaderClass)) { + if (logger.isDebugEnabled()) { + logger.debug("Found explicit ContextLoader [" + contextLoaderClass + + "] for @ContextConfiguration [" + contextConfiguration + "] and declaring class [" + + declaringClass + "]"); + } + return contextLoaderClass; + } + + declaringClass = AnnotationUtils.findAnnotationDeclaringClass(annotationType, + declaringClass.getSuperclass()); + } + + try { + ContextConfiguration contextConfiguration = clazz.getAnnotation(ContextConfiguration.class); + if (logger.isTraceEnabled()) { + logger.trace("Using default ContextLoader class [" + defaultContextLoaderClassName + + "] for @ContextConfiguration [" + contextConfiguration + "] and class [" + clazz + "]"); + } + return (Class) getClass().getClassLoader().loadClass(defaultContextLoaderClassName); + } + catch (ClassNotFoundException ex) { + throw new IllegalStateException("Could not load default ContextLoader class [" + + defaultContextLoaderClassName + "]. Specify @ContextConfiguration's 'loader' " + + "attribute or make the default loader class available."); + } + } + /** * Retrieve {@link ApplicationContext} resource locations for the supplied * {@link Class class}, using the supplied {@link ContextLoader} to @@ -157,10 +215,10 @@ public class TestContext extends AttributeAccessorSupport { * @ContextConfiguration} will be taken into consideration. * Specifically, if the inheritLocations flag is set to * true, locations defined in the annotated class will be - * appended to the locations defined in superclasses. @param - * contextLoader the ContextLoader to use for processing the locations (must - * not be null) + * appended to the locations defined in superclasses. * + * @param contextLoader the ContextLoader to use for processing the + * locations (must not be null) * @param clazz the class for which to retrieve the resource locations (must * not be null) * @return the list of ApplicationContext resource locations for the diff --git a/org.springframework.test/src/test/java/org/springframework/test/context/configuration/ContextConfigurationWithPropertiesExtendingPropertiesAndInheritedLoaderTests-context.properties b/org.springframework.test/src/test/java/org/springframework/test/context/configuration/ContextConfigurationWithPropertiesExtendingPropertiesAndInheritedLoaderTests-context.properties new file mode 100644 index 0000000000000000000000000000000000000000..1efbb6201e0a33bbab5843d117913e575e96a5ce --- /dev/null +++ b/org.springframework.test/src/test/java/org/springframework/test/context/configuration/ContextConfigurationWithPropertiesExtendingPropertiesAndInheritedLoaderTests-context.properties @@ -0,0 +1,5 @@ +dog.(class)=org.springframework.beans.Pet +dog.$0=Fido + +testString2.(class)=java.lang.String +testString2.$0=Test String #2 diff --git a/org.springframework.test/src/test/java/org/springframework/test/context/configuration/ContextConfigurationWithPropertiesExtendingPropertiesAndInheritedLoaderTests.java b/org.springframework.test/src/test/java/org/springframework/test/context/configuration/ContextConfigurationWithPropertiesExtendingPropertiesAndInheritedLoaderTests.java new file mode 100644 index 0000000000000000000000000000000000000000..a9b9e3f16f816605b9272a1063436af4fa863235 --- /dev/null +++ b/org.springframework.test/src/test/java/org/springframework/test/context/configuration/ContextConfigurationWithPropertiesExtendingPropertiesAndInheritedLoaderTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2009 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.configuration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; +import org.springframework.beans.Pet; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.ContextLoader; +import org.springframework.test.context.junit4.PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests; + +/** + * Integration tests which verify that the same custom {@link ContextLoader} can + * be used at all levels within a test class hierarchy when the + * loader is inherited (i.e., not explicitly declared) via + * {@link ContextConfiguration @ContextConfiguration}. + * + * @author Sam Brannen + * @since 3.0 + * @see PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests + * @see ContextConfigurationWithPropertiesExtendingPropertiesTests + */ +@ContextConfiguration +public class ContextConfigurationWithPropertiesExtendingPropertiesAndInheritedLoaderTests extends + PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests { + + @Autowired + private Pet dog; + + @Autowired + private String testString2; + + + @Test + public void verifyExtendedAnnotationAutowiredFields() { + assertNotNull("The dog field should have been autowired.", this.dog); + assertEquals("Fido", this.dog.getName()); + + assertNotNull("The testString2 field should have been autowired.", this.testString2); + assertEquals("Test String #2", this.testString2); + } + +} diff --git a/org.springframework.test/src/test/java/org/springframework/test/context/configuration/ContextConfigurationWithPropertiesExtendingPropertiesTests-context.properties b/org.springframework.test/src/test/java/org/springframework/test/context/configuration/ContextConfigurationWithPropertiesExtendingPropertiesTests-context.properties new file mode 100644 index 0000000000000000000000000000000000000000..1efbb6201e0a33bbab5843d117913e575e96a5ce --- /dev/null +++ b/org.springframework.test/src/test/java/org/springframework/test/context/configuration/ContextConfigurationWithPropertiesExtendingPropertiesTests-context.properties @@ -0,0 +1,5 @@ +dog.(class)=org.springframework.beans.Pet +dog.$0=Fido + +testString2.(class)=java.lang.String +testString2.$0=Test String #2 diff --git a/org.springframework.test/src/test/java/org/springframework/test/context/configuration/ContextConfigurationWithPropertiesExtendingPropertiesTests.java b/org.springframework.test/src/test/java/org/springframework/test/context/configuration/ContextConfigurationWithPropertiesExtendingPropertiesTests.java new file mode 100644 index 0000000000000000000000000000000000000000..3038750d41187e44360dbd2a16d4ce80c989165b --- /dev/null +++ b/org.springframework.test/src/test/java/org/springframework/test/context/configuration/ContextConfigurationWithPropertiesExtendingPropertiesTests.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2009 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.configuration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; +import org.springframework.beans.Pet; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.ContextLoader; +import org.springframework.test.context.junit4.PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests; +import org.springframework.test.context.support.GenericPropertiesContextLoader; + +/** + * Integration tests which verify that the same custom {@link ContextLoader} can + * be used at all levels within a test class hierarchy when the + * loader is explicitly declared via {@link ContextConfiguration + * @ContextConfiguration}. + * + * @author Sam Brannen + * @since 3.0 + * @see PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests + * @see ContextConfigurationWithPropertiesExtendingPropertiesAndInheritedLoaderTests + */ +@ContextConfiguration(loader = GenericPropertiesContextLoader.class) +public class ContextConfigurationWithPropertiesExtendingPropertiesTests extends + PropertiesBasedSpringJUnit4ClassRunnerAppCtxTests { + + @Autowired + private Pet dog; + + @Autowired + private String testString2; + + + @Test + public void verifyExtendedAnnotationAutowiredFields() { + assertNotNull("The dog field should have been autowired.", this.dog); + assertEquals("Fido", this.dog.getName()); + + assertNotNull("The testString2 field should have been autowired.", this.testString2); + assertEquals("Test String #2", this.testString2); + } + +}