Browse Source
Prior to this commit, the Spring TestContext Framework (TCF) supported the use of test-related annotations as meta-annotations for composing custom test stereotype annotations; however, attributes in custom stereotypes could not be used to override meta-annotation attributes. This commit addresses this by allowing attributes from the following annotations (when used as meta-annotations) to be overridden in custom stereotypes. - @ContextConfiguration - @ActiveProfiles - @DirtiesContext - @TransactionConfiguration - @Timed - @TestExecutionListeners This support depends on functionality provided by AnnotatedElementUtils. See the 'Notes' below for further details and ramifications. Notes: - AnnotatedElementUtils does not support overrides for the 'value' attribute of an annotation. It is therefore not possible or not feasible to support meta-annotation attribute overrides for some test-related annotations. - @ContextHierarchy, @WebAppConfiguration, @Rollback, @Repeat, and @ProfileValueSourceConfiguration define single 'value' attributes which cannot be overridden via Spring's meta-annotation attribute support. - Although @IfProfileValue has 'values' and 'name' attributes, the typical usage scenario involves the 'value' attribute which is not supported for meta-annotation attribute overrides. Furthermore, 'name' and 'values' are so generic that it is deemed unfeasible to provide meta-annotation attribute override support for these. - @BeforeTransaction and @AfterTransaction do not define any attributes that can be overridden. - Support for meta-annotation attribute overrides for @Transactional is provided indirectly via SpringTransactionAnnotationParser. Implementation Details: - MetaAnnotationUtils.AnnotationDescriptor now provides access to the AnnotationAttributes for the described annotation. - MetaAnnotationUtils.AnnotationDescriptor now provides access to the root declaring class as well as the declaring class. - ContextLoaderUtils now retrieves AnnotationAttributes from AnnotationDescriptor to look up annotation attributes for @ContextConfiguration and @ActiveProfiles. - ContextConfigurationAttributes now provides a constructor to have its attributes sourced from an instance of AnnotationAttributes. - ContextLoaderUtils.resolveContextHierarchyAttributes() now throws an IllegalStateException if no class in the class hierarchy declares @ContextHierarchy. - TransactionalTestExecutionListener now uses AnnotatedElementUtils to look up annotation attributes for @TransactionConfiguration. - Implemented missing unit tests for @Rollback resolution in TransactionalTestExecutionListener. - SpringJUnit4ClassRunner now uses AnnotatedElementUtils to look up annotation attributes for @Timed. - TestContextManager now retrieves AnnotationAttributes from AnnotationDescriptor to look up annotation attributes for @TestExecutionListeners. - DirtiesContextTestExecutionListener now uses AnnotatedElementUtils to look up annotation attributes for @DirtiesContext. Issue: SPR-11038pull/421/merge
21 changed files with 1097 additions and 162 deletions
@ -0,0 +1,155 @@
@@ -0,0 +1,155 @@
|
||||
/* |
||||
* 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.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.core.annotation.AnnotationAttributes; |
||||
import org.springframework.test.context.MetaAnnotationUtils.AnnotationDescriptor; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.springframework.test.context.MetaAnnotationUtils.*; |
||||
|
||||
/** |
||||
* Unit tests for {@link MetaAnnotationUtils} that verify support for overridden |
||||
* meta-annotation attributes. |
||||
* |
||||
* <p>See <a href="https://jira.springsource.org/browse/SPR-10181">SPR-10181</a>. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 4.0 |
||||
*/ |
||||
public class OverriddenMetaAnnotationAttributesTests { |
||||
|
||||
@Test |
||||
public void contextConfigurationValue() throws Exception { |
||||
Class<MetaValueConfigTest> declaringClass = MetaValueConfigTest.class; |
||||
AnnotationDescriptor<ContextConfiguration> descriptor = findAnnotationDescriptor(declaringClass, |
||||
ContextConfiguration.class); |
||||
assertNotNull(descriptor); |
||||
assertEquals(declaringClass, descriptor.getRootDeclaringClass()); |
||||
assertEquals(MetaValueConfig.class, descriptor.getStereotypeType()); |
||||
assertEquals(ContextConfiguration.class, descriptor.getAnnotationType()); |
||||
assertNotNull(descriptor.getStereotype()); |
||||
assertEquals(MetaValueConfig.class, descriptor.getStereotypeType()); |
||||
|
||||
// direct access to annotation value:
|
||||
assertArrayEquals(new String[] { "foo.xml" }, descriptor.getAnnotation().value()); |
||||
} |
||||
|
||||
@Test |
||||
public void overriddenContextConfigurationValue() throws Exception { |
||||
Class<?> declaringClass = OverriddenMetaValueConfigTest.class; |
||||
AnnotationDescriptor<ContextConfiguration> descriptor = findAnnotationDescriptor(declaringClass, |
||||
ContextConfiguration.class); |
||||
assertNotNull(descriptor); |
||||
assertEquals(declaringClass, descriptor.getRootDeclaringClass()); |
||||
assertEquals(MetaValueConfig.class, descriptor.getStereotypeType()); |
||||
assertEquals(ContextConfiguration.class, descriptor.getAnnotationType()); |
||||
assertNotNull(descriptor.getStereotype()); |
||||
assertEquals(MetaValueConfig.class, descriptor.getStereotypeType()); |
||||
|
||||
// direct access to annotation value:
|
||||
assertArrayEquals(new String[] { "foo.xml" }, descriptor.getAnnotation().value()); |
||||
|
||||
// overridden attribute:
|
||||
AnnotationAttributes attributes = descriptor.getAnnotationAttributes(); |
||||
|
||||
// NOTE: we would like to be able to override the 'value' attribute; however,
|
||||
// Spring currently does not allow overrides for the 'value' attribute.
|
||||
// TODO Determine if Spring should allow overrides for the 'value' attribute in
|
||||
// custom annotations.
|
||||
assertArrayEquals(new String[] { "foo.xml" }, attributes.getStringArray("value")); |
||||
} |
||||
|
||||
@Test |
||||
public void contextConfigurationLocationsAndInheritLocations() throws Exception { |
||||
Class<MetaLocationsConfigTest> declaringClass = MetaLocationsConfigTest.class; |
||||
AnnotationDescriptor<ContextConfiguration> descriptor = findAnnotationDescriptor(declaringClass, |
||||
ContextConfiguration.class); |
||||
assertNotNull(descriptor); |
||||
assertEquals(declaringClass, descriptor.getRootDeclaringClass()); |
||||
assertEquals(MetaLocationsConfig.class, descriptor.getStereotypeType()); |
||||
assertEquals(ContextConfiguration.class, descriptor.getAnnotationType()); |
||||
assertNotNull(descriptor.getStereotype()); |
||||
assertEquals(MetaLocationsConfig.class, descriptor.getStereotypeType()); |
||||
|
||||
// direct access to annotation attributes:
|
||||
assertArrayEquals(new String[] { "foo.xml" }, descriptor.getAnnotation().locations()); |
||||
assertFalse(descriptor.getAnnotation().inheritLocations()); |
||||
} |
||||
|
||||
@Test |
||||
public void overriddenContextConfigurationLocationsAndInheritLocations() throws Exception { |
||||
Class<?> declaringClass = OverriddenMetaLocationsConfigTest.class; |
||||
AnnotationDescriptor<ContextConfiguration> descriptor = findAnnotationDescriptor(declaringClass, |
||||
ContextConfiguration.class); |
||||
assertNotNull(descriptor); |
||||
assertEquals(declaringClass, descriptor.getRootDeclaringClass()); |
||||
assertEquals(MetaLocationsConfig.class, descriptor.getStereotypeType()); |
||||
assertEquals(ContextConfiguration.class, descriptor.getAnnotationType()); |
||||
assertNotNull(descriptor.getStereotype()); |
||||
assertEquals(MetaLocationsConfig.class, descriptor.getStereotypeType()); |
||||
|
||||
// direct access to annotation attributes:
|
||||
assertArrayEquals(new String[] { "foo.xml" }, descriptor.getAnnotation().locations()); |
||||
assertFalse(descriptor.getAnnotation().inheritLocations()); |
||||
|
||||
// overridden attributes:
|
||||
AnnotationAttributes attributes = descriptor.getAnnotationAttributes(); |
||||
assertArrayEquals(new String[] { "bar.xml" }, attributes.getStringArray("locations")); |
||||
assertTrue(attributes.getBoolean("inheritLocations")); |
||||
} |
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ContextConfiguration("foo.xml") |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
static @interface MetaValueConfig { |
||||
|
||||
String[] value() default {}; |
||||
} |
||||
|
||||
@MetaValueConfig |
||||
public static class MetaValueConfigTest { |
||||
} |
||||
|
||||
@MetaValueConfig("bar.xml") |
||||
public static class OverriddenMetaValueConfigTest { |
||||
} |
||||
|
||||
@ContextConfiguration(locations = "foo.xml", inheritLocations = false) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
static @interface MetaLocationsConfig { |
||||
|
||||
String[] locations() default {}; |
||||
|
||||
boolean inheritLocations(); |
||||
} |
||||
|
||||
@MetaLocationsConfig(inheritLocations = true) |
||||
static class MetaLocationsConfigTest { |
||||
} |
||||
|
||||
@MetaLocationsConfig(locations = "bar.xml", inheritLocations = true) |
||||
static class OverriddenMetaLocationsConfigTest { |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
/* |
||||
* 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.junit4.annotation.meta; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.Profile; |
||||
import org.springframework.test.context.ActiveProfiles; |
||||
import org.springframework.test.context.ContextConfiguration; |
||||
|
||||
/** |
||||
* Custom configuration annotation with meta-annotation attribute overrides for |
||||
* {@link ContextConfiguration#classes} and {@link ActiveProfiles#profiles}. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 4.0 |
||||
*/ |
||||
@ContextConfiguration |
||||
@ActiveProfiles |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.TYPE) |
||||
public @interface MetaConfig { |
||||
|
||||
@Configuration |
||||
@Profile("dev") |
||||
static class DevConfig { |
||||
|
||||
@Bean |
||||
public String foo() { |
||||
return "Dev Foo"; |
||||
} |
||||
} |
||||
|
||||
@Configuration |
||||
@Profile("prod") |
||||
static class ProductionConfig { |
||||
|
||||
@Bean |
||||
public String foo() { |
||||
return "Production Foo"; |
||||
} |
||||
} |
||||
|
||||
|
||||
Class<?>[] classes() default { DevConfig.class, ProductionConfig.class }; |
||||
|
||||
String[] profiles() default "dev"; |
||||
|
||||
} |
||||
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
/* |
||||
* 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.junit4.annotation.meta; |
||||
|
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
/** |
||||
* Integration tests for meta-annotation attribute override support, relying on |
||||
* default attribute values defined in {@link MetaConfig}. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 4.0 |
||||
*/ |
||||
@RunWith(SpringJUnit4ClassRunner.class) |
||||
@MetaConfig |
||||
public class MetaConfigDefaultsTests { |
||||
|
||||
@Autowired |
||||
private String foo; |
||||
|
||||
|
||||
@Test |
||||
public void foo() { |
||||
assertEquals("Dev Foo", foo); |
||||
} |
||||
} |
||||
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/* |
||||
* 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.junit4.annotation.meta; |
||||
|
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; |
||||
import org.springframework.test.context.junit4.annotation.PojoAndStringConfig; |
||||
import org.springframework.tests.sample.beans.Employee; |
||||
import org.springframework.tests.sample.beans.Pet; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
/** |
||||
* Integration tests for meta-annotation attribute override support, overriding |
||||
* default attribute values defined in {@link MetaConfig}. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 4.0 |
||||
*/ |
||||
@RunWith(SpringJUnit4ClassRunner.class) |
||||
@MetaConfig(classes = { PojoAndStringConfig.class, MetaConfig.ProductionConfig.class }, profiles = "prod") |
||||
public class MetaConfigOverrideTests { |
||||
|
||||
@Autowired |
||||
private String foo; |
||||
|
||||
@Autowired |
||||
private Pet pet; |
||||
|
||||
@Autowired |
||||
protected Employee employee; |
||||
|
||||
|
||||
@Test |
||||
public void verifyEmployee() { |
||||
assertNotNull("The employee should have been autowired.", this.employee); |
||||
assertEquals("John Smith", this.employee.getName()); |
||||
} |
||||
|
||||
@Test |
||||
public void verifyPet() { |
||||
assertNotNull("The pet should have been autowired.", this.pet); |
||||
assertEquals("Fido", this.pet.getName()); |
||||
} |
||||
|
||||
@Test |
||||
public void verifyFoo() { |
||||
assertEquals("Production Foo", this.foo); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue