Support meta-annotation attr overrides in the TCF

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-11038
This commit is contained in:
Sam Brannen
2013-11-20 19:44:55 +01:00
parent c5779e2ed6
commit 64f593db8f
21 changed files with 1099 additions and 164 deletions

View File

@@ -110,6 +110,17 @@ abstract class AbstractContextLoaderUtilsTests {
public static @interface MetaLocationsFooConfig {
}
@ContextConfiguration
@ActiveProfiles
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public static @interface MetaLocationsFooConfigWithOverrides {
String[] locations() default "/foo.xml";
String[] profiles() default "foo";
}
@ContextConfiguration("/bar.xml")
@ActiveProfiles(profiles = "bar")
@Retention(RetentionPolicy.RUNTIME)
@@ -125,6 +136,14 @@ abstract class AbstractContextLoaderUtilsTests {
static class MetaLocationsBar extends MetaLocationsFoo {
}
@MetaLocationsFooConfigWithOverrides
static class MetaLocationsFooWithOverrides {
}
@MetaLocationsFooConfigWithOverrides(locations = { "foo1.xml", "foo2.xml" }, profiles = { "foo1", "foo2" })
static class MetaLocationsFooWithOverriddenAttributes {
}
@ContextConfiguration(locations = "/foo.xml", inheritLocations = false)
@ActiveProfiles(profiles = "foo")
static class LocationsFoo {

View File

@@ -121,6 +121,26 @@ public class ContextLoaderUtilsActiveProfilesTests extends AbstractContextLoader
assertArrayEquals(new String[] { "foo" }, profiles);
}
/**
* @since 4.0
*/
@Test
public void resolveActiveProfilesWithMetaAnnotationAndOverrides() {
String[] profiles = resolveActiveProfiles(MetaLocationsFooWithOverrides.class);
assertNotNull(profiles);
assertArrayEquals(new String[] { "foo" }, profiles);
}
/**
* @since 4.0
*/
@Test
public void resolveActiveProfilesWithMetaAnnotationAndOverriddenAttributes() {
String[] profiles = resolveActiveProfiles(MetaLocationsFooWithOverriddenAttributes.class);
assertNotNull(profiles);
assertArrayEquals(new String[] { "foo1", "foo2" }, profiles);
}
/**
* @since 4.0
*/
@@ -256,6 +276,18 @@ public class ContextLoaderUtilsActiveProfilesTests extends AbstractContextLoader
private static @interface MetaAnimalsConfig {
}
@ActiveProfiles
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
private static @interface MetaProfilesWithOverrides {
String[] profiles() default { "dog", "cat" };
Class<? extends ActiveProfilesResolver> resolver() default ActiveProfilesResolver.class;
boolean inheritProfiles() default false;
}
@MetaAnimalsConfig
private static class MetaAnimals extends MetaLocationsBar {
}

View File

@@ -83,6 +83,24 @@ public class ContextLoaderUtilsConfigurationAttributesTests extends AbstractCont
EMPTY_CLASS_ARRAY, ContextLoader.class, true);
}
@Test
public void resolveConfigAttributesWithMetaAnnotationAndLocationsAndOverrides() {
List<ContextConfigurationAttributes> attributesList = resolveContextConfigurationAttributes(MetaLocationsFooWithOverrides.class);
assertNotNull(attributesList);
assertEquals(1, attributesList.size());
assertAttributes(attributesList.get(0), MetaLocationsFooConfigWithOverrides.class, new String[] { "/foo.xml" },
EMPTY_CLASS_ARRAY, ContextLoader.class, true);
}
@Test
public void resolveConfigAttributesWithMetaAnnotationAndLocationsAndOverriddenAttributes() {
List<ContextConfigurationAttributes> attributesList = resolveContextConfigurationAttributes(MetaLocationsFooWithOverriddenAttributes.class);
assertNotNull(attributesList);
assertEquals(1, attributesList.size());
assertAttributes(attributesList.get(0), MetaLocationsFooConfigWithOverrides.class, new String[] { "foo1.xml",
"foo2.xml" }, EMPTY_CLASS_ARRAY, ContextLoader.class, true);
}
@Test
public void resolveConfigAttributesWithMetaAnnotationAndLocationsInClassHierarchy() {
List<ContextConfigurationAttributes> attributesList = resolveContextConfigurationAttributes(MetaLocationsBar.class);

View File

@@ -55,13 +55,9 @@ public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextLoad
resolveContextHierarchyAttributes(SingleTestClassWithContextConfigurationAndContextHierarchyOnSingleMetaAnnotation.class);
}
@Test
@Test(expected = IllegalStateException.class)
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);
resolveContextHierarchyAttributes(BareAnnotations.class);
}
@Test
@@ -98,8 +94,8 @@ public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextLoad
debugConfigAttributes(configAttributesList);
assertAttributes(configAttributesList.get(0), testClass, new String[] { "A.xml" }, EMPTY_CLASS_ARRAY,
ContextLoader.class, true);
assertAttributes(configAttributesList.get(1), testClass, new String[] { "B.xml" },
EMPTY_CLASS_ARRAY, ContextLoader.class, true);
assertAttributes(configAttributesList.get(1), testClass, new String[] { "B.xml" }, EMPTY_CLASS_ARRAY,
ContextLoader.class, true);
assertAttributes(configAttributesList.get(2), testClass, new String[] { "C.xml" }, EMPTY_CLASS_ARRAY,
ContextLoader.class, true);
}
@@ -154,36 +150,39 @@ public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextLoad
EMPTY_CLASS_ARRAY, ContextLoader.class, true);
}
@Test
public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSubclass() {
List<List<ContextConfigurationAttributes>> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSubclass.class);
private void assertOneTwo(List<List<ContextConfigurationAttributes>> hierarchyAttributes) {
assertEquals(2, hierarchyAttributes.size());
List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0);
List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1);
debugConfigAttributes(configAttributesListClassLevel1);
debugConfigAttributes(configAttributesListClassLevel2);
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());
assertOneTwo(resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSuperclass.class));
}
List<ContextConfigurationAttributes> configAttributesListClassLevel1 = hierarchyAttributes.get(0);
debugConfigAttributes(configAttributesListClassLevel1);
assertEquals(1, configAttributesListClassLevel1.size());
assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml"));
@Test
public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSubclass() {
assertOneTwo(resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSubclass.class));
}
List<ContextConfigurationAttributes> configAttributesListClassLevel2 = hierarchyAttributes.get(1);
debugConfigAttributes(configAttributesListClassLevel2);
assertEquals(1, configAttributesListClassLevel2.size());
assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml"));
@Test
public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareMetaContextConfigWithOverridesInSuperclass() {
assertOneTwo(resolveContextHierarchyAttributes(TestClass2WithBareMetaContextConfigWithOverridesInSuperclass.class));
}
@Test
public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareMetaContextConfigWithOverridesInSubclass() {
assertOneTwo(resolveContextHierarchyAttributes(TestClass2WithBareMetaContextConfigWithOverridesInSubclass.class));
}
@Test
@@ -408,7 +407,7 @@ public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextLoad
@ContextConfiguration("two.xml")
private static class TestClass2WithBareContextConfigurationInSubclass extends
TestClass1WithBareContextConfigurationInSuperclass {
TestClass1WithBareContextConfigurationInSubclass {
}
@ContextHierarchy({//
@@ -569,4 +568,31 @@ public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextLoad
TestClass2WithSingleLevelContextHierarchyFromMetaAnnotation {
}
// -------------------------------------------------------------------------
@ContextConfiguration
@Retention(RetentionPolicy.RUNTIME)
private static @interface ContextConfigWithOverrides {
String[] locations() default "A.xml";
}
@ContextConfigWithOverrides(locations = "one.xml")
private static class TestClass1WithBareMetaContextConfigWithOverridesInSuperclass {
}
@ContextHierarchy(@ContextConfiguration(locations = "two.xml"))
private static class TestClass2WithBareMetaContextConfigWithOverridesInSuperclass extends
TestClass1WithBareMetaContextConfigWithOverridesInSuperclass {
}
@ContextHierarchy(@ContextConfiguration(locations = "one.xml"))
private static class TestClass1WithBareMetaContextConfigWithOverridesInSubclass {
}
@ContextConfigWithOverrides(locations = "two.xml")
private static class TestClass2WithBareMetaContextConfigWithOverridesInSubclass extends
TestClass1WithBareMetaContextConfigWithOverridesInSubclass {
}
}

View File

@@ -43,7 +43,7 @@ public class MetaAnnotationUtilsTests {
Class<? extends Annotation> stereotypeType) {
AnnotationDescriptor<Component> descriptor = findAnnotationDescriptor(startClass, Component.class);
assertNotNull(descriptor);
assertEquals(declaringClass, descriptor.getDeclaringClass());
assertEquals(declaringClass, descriptor.getRootDeclaringClass());
assertEquals(Component.class, descriptor.getAnnotationType());
assertEquals(name, descriptor.getAnnotation().value());
assertNotNull(descriptor.getStereotype());
@@ -57,7 +57,7 @@ public class MetaAnnotationUtilsTests {
UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(startClass, Service.class,
annotationType, Order.class, Transactional.class);
assertNotNull(descriptor);
assertEquals(declaringClass, descriptor.getDeclaringClass());
assertEquals(declaringClass, descriptor.getRootDeclaringClass());
assertEquals(annotationType, descriptor.getAnnotationType());
assertEquals(name, ((Component) descriptor.getAnnotation()).value());
assertNotNull(descriptor.getStereotype());
@@ -74,16 +74,16 @@ public class MetaAnnotationUtilsTests {
public void findAnnotationDescriptorWithInheritedAnnotationOnClass() throws Exception {
// Note: @Transactional is inherited
assertEquals(InheritedAnnotationClass.class,
findAnnotationDescriptor(InheritedAnnotationClass.class, Transactional.class).getDeclaringClass());
findAnnotationDescriptor(InheritedAnnotationClass.class, Transactional.class).getRootDeclaringClass());
assertEquals(InheritedAnnotationClass.class,
findAnnotationDescriptor(SubInheritedAnnotationClass.class, Transactional.class).getDeclaringClass());
findAnnotationDescriptor(SubInheritedAnnotationClass.class, Transactional.class).getRootDeclaringClass());
}
@Test
public void findAnnotationDescriptorWithInheritedAnnotationOnInterface() throws Exception {
// Note: @Transactional is inherited
assertEquals(InheritedAnnotationInterface.class,
findAnnotationDescriptor(InheritedAnnotationInterface.class, Transactional.class).getDeclaringClass());
findAnnotationDescriptor(InheritedAnnotationInterface.class, Transactional.class).getRootDeclaringClass());
assertNull(findAnnotationDescriptor(SubInheritedAnnotationInterface.class, Transactional.class));
assertNull(findAnnotationDescriptor(SubSubInheritedAnnotationInterface.class, Transactional.class));
}
@@ -92,16 +92,16 @@ public class MetaAnnotationUtilsTests {
public void findAnnotationDescriptorForNonInheritedAnnotationOnClass() throws Exception {
// Note: @Order is not inherited.
assertEquals(NonInheritedAnnotationClass.class,
findAnnotationDescriptor(NonInheritedAnnotationClass.class, Order.class).getDeclaringClass());
findAnnotationDescriptor(NonInheritedAnnotationClass.class, Order.class).getRootDeclaringClass());
assertEquals(NonInheritedAnnotationClass.class,
findAnnotationDescriptor(SubNonInheritedAnnotationClass.class, Order.class).getDeclaringClass());
findAnnotationDescriptor(SubNonInheritedAnnotationClass.class, Order.class).getRootDeclaringClass());
}
@Test
public void findAnnotationDescriptorForNonInheritedAnnotationOnInterface() throws Exception {
// Note: @Order is not inherited.
assertEquals(NonInheritedAnnotationInterface.class,
findAnnotationDescriptor(NonInheritedAnnotationInterface.class, Order.class).getDeclaringClass());
findAnnotationDescriptor(NonInheritedAnnotationInterface.class, Order.class).getRootDeclaringClass());
assertNull(findAnnotationDescriptor(SubNonInheritedAnnotationInterface.class, Order.class));
}
@@ -116,7 +116,7 @@ public class MetaAnnotationUtilsTests {
Class<Component> annotationType = Component.class;
AnnotationDescriptor<Component> descriptor = findAnnotationDescriptor(HasLocalAndMetaComponentAnnotation.class,
annotationType);
assertEquals(HasLocalAndMetaComponentAnnotation.class, descriptor.getDeclaringClass());
assertEquals(HasLocalAndMetaComponentAnnotation.class, descriptor.getRootDeclaringClass());
assertEquals(annotationType, descriptor.getAnnotationType());
assertNull(descriptor.getStereotype());
assertNull(descriptor.getStereotypeType());
@@ -159,10 +159,10 @@ public class MetaAnnotationUtilsTests {
public void findAnnotationDescriptorForTypesWithInheritedAnnotationOnClass() throws Exception {
// Note: @Transactional is inherited
assertEquals(InheritedAnnotationClass.class,
findAnnotationDescriptorForTypes(InheritedAnnotationClass.class, Transactional.class).getDeclaringClass());
findAnnotationDescriptorForTypes(InheritedAnnotationClass.class, Transactional.class).getRootDeclaringClass());
assertEquals(
InheritedAnnotationClass.class,
findAnnotationDescriptorForTypes(SubInheritedAnnotationClass.class, Transactional.class).getDeclaringClass());
findAnnotationDescriptorForTypes(SubInheritedAnnotationClass.class, Transactional.class).getRootDeclaringClass());
}
@Test
@@ -171,7 +171,7 @@ public class MetaAnnotationUtilsTests {
// Note: @Transactional is inherited
assertEquals(
InheritedAnnotationInterface.class,
findAnnotationDescriptorForTypes(InheritedAnnotationInterface.class, Transactional.class).getDeclaringClass());
findAnnotationDescriptorForTypes(InheritedAnnotationInterface.class, Transactional.class).getRootDeclaringClass());
assertNull(findAnnotationDescriptorForTypes(SubInheritedAnnotationInterface.class, Transactional.class));
assertNull(findAnnotationDescriptorForTypes(SubSubInheritedAnnotationInterface.class, Transactional.class));
}
@@ -181,9 +181,9 @@ public class MetaAnnotationUtilsTests {
public void findAnnotationDescriptorForTypesForNonInheritedAnnotationOnClass() throws Exception {
// Note: @Order is not inherited.
assertEquals(NonInheritedAnnotationClass.class,
findAnnotationDescriptorForTypes(NonInheritedAnnotationClass.class, Order.class).getDeclaringClass());
findAnnotationDescriptorForTypes(NonInheritedAnnotationClass.class, Order.class).getRootDeclaringClass());
assertEquals(NonInheritedAnnotationClass.class,
findAnnotationDescriptorForTypes(SubNonInheritedAnnotationClass.class, Order.class).getDeclaringClass());
findAnnotationDescriptorForTypes(SubNonInheritedAnnotationClass.class, Order.class).getRootDeclaringClass());
}
@Test
@@ -191,7 +191,7 @@ public class MetaAnnotationUtilsTests {
public void findAnnotationDescriptorForTypesForNonInheritedAnnotationOnInterface() throws Exception {
// Note: @Order is not inherited.
assertEquals(NonInheritedAnnotationInterface.class,
findAnnotationDescriptorForTypes(NonInheritedAnnotationInterface.class, Order.class).getDeclaringClass());
findAnnotationDescriptorForTypes(NonInheritedAnnotationInterface.class, Order.class).getRootDeclaringClass());
assertNull(findAnnotationDescriptorForTypes(SubNonInheritedAnnotationInterface.class, Order.class));
}
@@ -201,7 +201,7 @@ public class MetaAnnotationUtilsTests {
Class<Component> annotationType = Component.class;
UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(
HasLocalAndMetaComponentAnnotation.class, Transactional.class, annotationType, Order.class);
assertEquals(HasLocalAndMetaComponentAnnotation.class, descriptor.getDeclaringClass());
assertEquals(HasLocalAndMetaComponentAnnotation.class, descriptor.getRootDeclaringClass());
assertEquals(annotationType, descriptor.getAnnotationType());
assertNull(descriptor.getStereotype());
assertNull(descriptor.getStereotypeType());
@@ -213,6 +213,44 @@ public class MetaAnnotationUtilsTests {
assertComponentOnStereotypeForMultipleCandidateTypes(startClass, startClass, "meta1", Meta1.class);
}
@Test
@SuppressWarnings("unchecked")
public void findAnnotationDescriptorForTypesWithMetaAnnotationWithDefaultAttributes() throws Exception {
Class<?> startClass = MetaConfigWithDefaultAttributesTestCase.class;
Class<ContextConfiguration> annotationType = ContextConfiguration.class;
UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(startClass, Service.class,
ContextConfiguration.class, Order.class, Transactional.class);
assertNotNull(descriptor);
assertEquals(startClass, descriptor.getRootDeclaringClass());
assertEquals(annotationType, descriptor.getAnnotationType());
assertArrayEquals(new Class[] {}, ((ContextConfiguration) descriptor.getAnnotation()).value());
assertArrayEquals(new Class[] { MetaConfig.DevConfig.class, MetaConfig.ProductionConfig.class },
descriptor.getAnnotationAttributes().getClassArray("classes"));
assertNotNull(descriptor.getStereotype());
assertEquals(MetaConfig.class, descriptor.getStereotypeType());
}
@Test
@SuppressWarnings("unchecked")
public void findAnnotationDescriptorForTypesWithMetaAnnotationWithOverriddenAttributes() throws Exception {
Class<?> startClass = MetaConfigWithOverriddenAttributesTestCase.class;
Class<ContextConfiguration> annotationType = ContextConfiguration.class;
UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(startClass, Service.class,
ContextConfiguration.class, Order.class, Transactional.class);
assertNotNull(descriptor);
assertEquals(startClass, descriptor.getRootDeclaringClass());
assertEquals(annotationType, descriptor.getAnnotationType());
assertArrayEquals(new Class[] {}, ((ContextConfiguration) descriptor.getAnnotation()).value());
assertArrayEquals(new Class[] { MetaAnnotationUtilsTests.class },
descriptor.getAnnotationAttributes().getClassArray("classes"));
assertNotNull(descriptor.getStereotype());
assertEquals(MetaConfig.class, descriptor.getStereotypeType());
}
@Test
public void findAnnotationDescriptorForTypesForInterfaceWithMetaAnnotation() {
Class<InterfaceWithMetaAnnotation> startClass = InterfaceWithMetaAnnotation.class;
@@ -279,6 +317,28 @@ public class MetaAnnotationUtilsTests {
ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface {
}
@ContextConfiguration
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaConfig {
static class DevConfig {
}
static class ProductionConfig {
}
Class<?>[] classes() default { DevConfig.class, ProductionConfig.class };
}
@MetaConfig
public class MetaConfigWithDefaultAttributesTestCase {
}
@MetaConfig(classes = MetaAnnotationUtilsTests.class)
public class MetaConfigWithOverriddenAttributesTestCase {
}
// -------------------------------------------------------------------------
@Transactional

View File

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

View File

@@ -16,14 +16,14 @@
package org.springframework.test.context;
import static org.junit.Assert.assertEquals;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.junit.Test;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import static org.junit.Assert.*;
/**
* <p>
* JUnit 4 based unit test for the {@link TestExecutionListeners
@@ -114,6 +114,29 @@ public class TestExecutionListenersTests {
testContextManager.getTestExecutionListeners().size());
}
@Test
public void verifyNumListenersRegisteredViaMetaAnnotationWithOverrides() throws Exception {
TestContextManager testContextManager = new TestContextManager(MetaWithOverridesExampleTestCase.class);
assertEquals("Num registered TELs for MetaWithOverridesExampleTestCase.", 3,
testContextManager.getTestExecutionListeners().size());
}
@Test
public void verifyNumListenersRegisteredViaMetaAnnotationWithInheritedListenersWithOverrides() throws Exception {
TestContextManager testContextManager = new TestContextManager(
MetaInheritedListenersWithOverridesExampleTestCase.class);
assertEquals("Num registered TELs for MetaInheritedListenersWithOverridesExampleTestCase.", 5,
testContextManager.getTestExecutionListeners().size());
}
@Test
public void verifyNumListenersRegisteredViaMetaAnnotationWithNonInheritedListenersWithOverrides() throws Exception {
TestContextManager testContextManager = new TestContextManager(
MetaNonInheritedListenersWithOverridesExampleTestCase.class);
assertEquals("Num registered TELs for MetaNonInheritedListenersWithOverridesExampleTestCase.", 8,
testContextManager.getTestExecutionListeners().size());
}
@Test(expected = IllegalStateException.class)
public void verifyDuplicateListenersConfigThrowsException() throws Exception {
new TestContextManager(DuplicateListenersConfigExampleTestCase.class);
@@ -174,6 +197,32 @@ public class TestExecutionListenersTests {
static @interface MetaNonInheritedListeners {
}
@TestExecutionListeners
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaListenersWithOverrides {
Class<? extends TestExecutionListener>[] listeners() default { FooTestExecutionListener.class,
BarTestExecutionListener.class };
}
@TestExecutionListeners
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaInheritedListenersWithOverrides {
Class<? extends TestExecutionListener>[] listeners() default QuuxTestExecutionListener.class;
boolean inheritListeners() default true;
}
@TestExecutionListeners
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaNonInheritedListenersWithOverrides {
Class<? extends TestExecutionListener>[] listeners() default QuuxTestExecutionListener.class;
boolean inheritListeners() default false;
}
@MetaListeners
static class MetaExampleTestCase {
}
@@ -186,6 +235,28 @@ public class TestExecutionListenersTests {
static class MetaNonInheritedListenersExampleTestCase extends MetaInheritedListenersExampleTestCase {
}
@MetaListenersWithOverrides(listeners = {//
FooTestExecutionListener.class,//
BarTestExecutionListener.class,//
BazTestExecutionListener.class //
})
static class MetaWithOverridesExampleTestCase {
}
@MetaInheritedListenersWithOverrides(listeners = { FooTestExecutionListener.class, BarTestExecutionListener.class })
static class MetaInheritedListenersWithOverridesExampleTestCase extends MetaWithOverridesExampleTestCase {
}
@MetaNonInheritedListenersWithOverrides(listeners = {//
FooTestExecutionListener.class,//
BarTestExecutionListener.class,//
BazTestExecutionListener.class //
},//
inheritListeners = true)
static class MetaNonInheritedListenersWithOverridesExampleTestCase extends
MetaInheritedListenersWithOverridesExampleTestCase {
}
static class FooTestExecutionListener extends AbstractTestExecutionListener {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 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,21 @@
package org.springframework.test.context.junit4;
import org.junit.Test;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.junit.Test;
import org.junit.runners.model.FrameworkMethod;
import org.springframework.test.annotation.Timed;
import org.springframework.test.context.TestContextManager;
import static org.junit.Assert.*;
/**
* @author Rick Evans
* Unit tests for {@link SpringJUnit4ClassRunner}.
*
* @author Sam Brannen
* @author Rick Evans
* @since 2.5
*/
public class SpringJUnit4ClassRunnerTests {
@@ -37,7 +45,8 @@ public class SpringJUnit4ClassRunnerTests {
@Override
public void prepareTestInstance(Object testInstance) {
throw new RuntimeException("This RuntimeException should be caught and wrapped in an Exception.");
throw new RuntimeException(
"This RuntimeException should be caught and wrapped in an Exception.");
}
};
}
@@ -45,4 +54,45 @@ public class SpringJUnit4ClassRunnerTests {
runner.createTest();
}
@Test
public void getSpringTimeoutViaMetaAnnotation() throws Exception {
SpringJUnit4ClassRunner runner = new SpringJUnit4ClassRunner(getClass());
long timeout = runner.getSpringTimeout(new FrameworkMethod(getClass().getDeclaredMethod(
"springTimeoutWithMetaAnnotation")));
assertEquals(10, timeout);
}
@Test
public void getSpringTimeoutViaMetaAnnotationWithOverride() throws Exception {
SpringJUnit4ClassRunner runner = new SpringJUnit4ClassRunner(getClass());
long timeout = runner.getSpringTimeout(new FrameworkMethod(getClass().getDeclaredMethod(
"springTimeoutWithMetaAnnotationAndOverride")));
assertEquals(42, timeout);
}
// -------------------------------------------------------------------------
@MetaTimed
void springTimeoutWithMetaAnnotation() {
/* no-op */
}
@MetaTimedWithOverride(millis = 42)
void springTimeoutWithMetaAnnotationAndOverride() {
/* no-op */
}
@Timed(millis = 10)
@Retention(RetentionPolicy.RUNTIME)
private static @interface MetaTimed {
}
@Timed(millis = 1000)
@Retention(RetentionPolicy.RUNTIME)
private static @interface MetaTimedWithOverride {
long millis() default 1000;
}
}

View File

@@ -16,8 +16,6 @@
package org.springframework.test.context.junit4;
import static org.junit.Assert.*;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -31,6 +29,8 @@ import org.springframework.test.context.TestExecutionListeners;
import org.springframework.tests.Assume;
import org.springframework.tests.TestGroup;
import static org.junit.Assert.*;
/**
* Verifies proper handling of the following in conjunction with the
* {@link SpringJUnit4ClassRunner}:
@@ -54,11 +54,11 @@ public class TimedSpringRunnerTests {
notifier.addListener(listener);
new SpringJUnit4ClassRunner(testClass).run(notifier);
assertEquals("Verifying number of tests started for test class [" + testClass + "].", 6,
assertEquals("Verifying number of tests started for test class [" + testClass + "].", 7,
listener.getTestStartedCount());
assertEquals("Verifying number of failures for test class [" + testClass + "].", 4,
assertEquals("Verifying number of failures for test class [" + testClass + "].", 5,
listener.getTestFailureCount());
assertEquals("Verifying number of tests finished for test class [" + testClass + "].", 6,
assertEquals("Verifying number of tests finished for test class [" + testClass + "].", 7,
listener.getTestFinishedCount());
}
@@ -101,6 +101,13 @@ public class TimedSpringRunnerTests {
Thread.sleep(20);
}
// Should Fail due to timeout.
@Test
@MetaTimedWithOverride(millis = 10)
public void springTimeoutWithSleepAndMetaAnnotationAndOverride() throws Exception {
Thread.sleep(20);
}
// Should Fail due to duplicate configuration.
@Test(timeout = 200)
@Timed(millis = 200)
@@ -114,4 +121,11 @@ public class TimedSpringRunnerTests {
private static @interface MetaTimed {
}
@Timed(millis = 1000)
@Retention(RetentionPolicy.RUNTIME)
private static @interface MetaTimedWithOverride {
long millis() default 1000;
}
}

View File

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

View File

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

View File

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

View File

@@ -26,8 +26,8 @@ import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.TestContext;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.annotation.DirtiesContext.ClassMode.*;
import static org.springframework.test.annotation.DirtiesContext.HierarchyMode.*;
/**
@@ -84,7 +84,7 @@ public class DirtiesContextTestExecutionListenerTests {
Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz);
when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("clean"));
listener.afterTestMethod(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
verify(testContext, times(0)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
@@ -93,7 +93,16 @@ public class DirtiesContextTestExecutionListenerTests {
Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz);
when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("clean"));
listener.afterTestMethod(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
verify(testContext, times(0)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
public void afterTestMethodForDirtiesContextViaMetaAnnotationWithOverrides() throws Exception {
Class<?> clazz = DirtiesContextViaMetaAnnotationWithOverrides.class;
Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz);
when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("clean"));
listener.afterTestMethod(testContext);
verify(testContext, times(1)).markApplicationContextDirty(CURRENT_LEVEL);
}
// -------------------------------------------------------------------------
@@ -103,7 +112,7 @@ public class DirtiesContextTestExecutionListenerTests {
Class<?> clazz = getClass();
Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz);
listener.afterTestClass(testContext);
verify(testContext, times(0)).markApplicationContextDirty(any(HierarchyMode.class));
verify(testContext, times(0)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
@@ -127,7 +136,7 @@ public class DirtiesContextTestExecutionListenerTests {
Class<?> clazz = DirtiesContextDeclaredLocallyAfterClass.class;
Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz);
listener.afterTestClass(testContext);
verify(testContext, times(1)).markApplicationContextDirty(any(HierarchyMode.class));
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
@@ -135,7 +144,23 @@ public class DirtiesContextTestExecutionListenerTests {
Class<?> clazz = DirtiesContextDeclaredViaMetaAnnotationAfterClass.class;
Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz);
listener.afterTestClass(testContext);
verify(testContext, times(1)).markApplicationContextDirty(any(HierarchyMode.class));
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
@Test
public void afterTestClassForDirtiesContextDeclaredViaMetaAnnotationWithOverrides() throws Exception {
Class<?> clazz = DirtiesContextViaMetaAnnotationWithOverrides.class;
Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz);
listener.afterTestClass(testContext);
verify(testContext, times(1)).markApplicationContextDirty(CURRENT_LEVEL);
}
@Test
public void afterTestClassForDirtiesContextDeclaredViaMetaAnnotationWithOverridenAttributes() throws Exception {
Class<?> clazz = DirtiesContextViaMetaAnnotationWithOverridenAttributes.class;
Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz);
listener.afterTestClass(testContext);
verify(testContext, times(1)).markApplicationContextDirty(EXHAUSTIVE);
}
// -------------------------------------------------------------------------
@@ -156,17 +181,17 @@ public class DirtiesContextTestExecutionListenerTests {
static @interface MetaDirty {
}
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaDirtyAfterEachTestMethod {
}
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
@DirtiesContext(classMode = AFTER_CLASS)
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaDirtyAfterClass {
}
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
static class DirtiesContextDeclaredLocallyAfterEachTestMethod {
void clean() {
@@ -174,6 +199,15 @@ public class DirtiesContextTestExecutionListenerTests {
}
}
@DirtiesContext
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaDirtyWithOverrides {
ClassMode classMode() default AFTER_EACH_TEST_METHOD;
HierarchyMode hierarchyMode() default HierarchyMode.CURRENT_LEVEL;
}
@MetaDirtyAfterEachTestMethod
static class DirtiesContextDeclaredViaMetaAnnotationAfterEachTestMethod {
@@ -182,7 +216,7 @@ public class DirtiesContextTestExecutionListenerTests {
}
}
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
@DirtiesContext(classMode = AFTER_CLASS)
static class DirtiesContextDeclaredLocallyAfterClass {
void clean() {
@@ -198,4 +232,20 @@ public class DirtiesContextTestExecutionListenerTests {
}
}
@MetaDirtyWithOverrides
static class DirtiesContextViaMetaAnnotationWithOverrides {
void clean() {
/* no-op */
}
}
@MetaDirtyWithOverrides(classMode = AFTER_CLASS, hierarchyMode = EXHAUSTIVE)
static class DirtiesContextViaMetaAnnotationWithOverridenAttributes {
void clean() {
/* no-op */
}
}
}

View File

@@ -21,14 +21,17 @@ import java.lang.annotation.RetentionPolicy;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.TestContext;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.SimpleTransactionStatus;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.springframework.transaction.annotation.Propagation.*;
/**
* Unit tests for {@link TransactionalTestExecutionListener}.
@@ -56,6 +59,11 @@ public class TransactionalTestExecutionListenerTests {
}
private void assertBeforeTestMethodWithTransactionalTestMethod(Class<? extends Invocable> clazz) throws Exception {
assertBeforeTestMethodWithTransactionalTestMethod(clazz, true);
}
private void assertBeforeTestMethodWithTransactionalTestMethod(Class<? extends Invocable> clazz, boolean invokedInTx)
throws Exception {
Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz);
Invocable instance = clazz.newInstance();
when(testContext.getTestInstance()).thenReturn(instance);
@@ -63,7 +71,7 @@ public class TransactionalTestExecutionListenerTests {
assertFalse(instance.invoked);
listener.beforeTestMethod(testContext);
assertTrue(instance.invoked);
assertEquals(invokedInTx, instance.invoked);
}
private void assertBeforeTestMethodWithNonTransactionalTestMethod(Class<? extends Invocable> clazz)
@@ -109,6 +117,22 @@ public class TransactionalTestExecutionListenerTests {
assertFalse(instance.invoked);
}
private void assertTransactionConfigurationAttributes(Class<?> clazz, String transactionManagerName,
boolean defaultRollback) {
Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz);
TransactionConfigurationAttributes attributes = listener.retrieveConfigurationAttributes(testContext);
assertNotNull(attributes);
assertEquals(transactionManagerName, attributes.getTransactionManagerName());
assertEquals(defaultRollback, attributes.isDefaultRollback());
}
private void assertIsRollback(Class<?> clazz, boolean rollback) throws NoSuchMethodException, Exception {
Mockito.<Class<?>> when(testContext.getTestClass()).thenReturn(clazz);
when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("test"));
assertEquals(rollback, listener.isRollback(testContext));
}
@Test
public void beforeTestMethodWithTransactionalDeclaredOnClassLocally() throws Exception {
assertBeforeTestMethodWithTransactionalTestMethod(TransactionalDeclaredOnClassLocallyTestCase.class);
@@ -119,6 +143,23 @@ public class TransactionalTestExecutionListenerTests {
assertBeforeTestMethodWithTransactionalTestMethod(TransactionalDeclaredOnClassViaMetaAnnotationTestCase.class);
}
@Test
public void beforeTestMethodWithTransactionalDeclaredOnClassViaMetaAnnotationWithOverride() throws Exception {
// Note: not actually invoked within a transaction since the test class is
// annotated with @MetaTxWithOverride(propagation = NOT_SUPPORTED)
assertBeforeTestMethodWithTransactionalTestMethod(
TransactionalDeclaredOnClassViaMetaAnnotationWithOverrideTestCase.class, false);
}
@Test
public void beforeTestMethodWithTransactionalDeclaredOnMethodViaMetaAnnotationWithOverride() throws Exception {
// Note: not actually invoked within a transaction since the method is
// annotated with @MetaTxWithOverride(propagation = NOT_SUPPORTED)
assertBeforeTestMethodWithTransactionalTestMethod(
TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase.class, false);
assertBeforeTestMethodWithNonTransactionalTestMethod(TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase.class);
}
@Test
public void beforeTestMethodWithTransactionalDeclaredOnMethodLocally() throws Exception {
assertBeforeTestMethod(TransactionalDeclaredOnMethodLocallyTestCase.class);
@@ -149,6 +190,55 @@ public class TransactionalTestExecutionListenerTests {
assertAfterTestMethod(AfterTransactionDeclaredViaMetaAnnotationTestCase.class);
}
@Test
public void retrieveConfigurationAttributesWithMissingTransactionConfiguration() throws Exception {
assertTransactionConfigurationAttributes(MissingTransactionConfigurationTestCase.class, "transactionManager",
true);
}
@Test
public void retrieveConfigurationAttributesWithEmptyTransactionConfiguration() throws Exception {
assertTransactionConfigurationAttributes(EmptyTransactionConfigurationTestCase.class, "transactionManager",
true);
}
@Test
public void retrieveConfigurationAttributesWithExplicitValues() throws Exception {
assertTransactionConfigurationAttributes(TransactionConfigurationWithExplicitValuesTestCase.class, "tm", false);
}
@Test
public void retrieveConfigurationAttributesViaMetaAnnotation() throws Exception {
assertTransactionConfigurationAttributes(TransactionConfigurationViaMetaAnnotationTestCase.class, "metaTxMgr",
true);
}
@Test
public void retrieveConfigurationAttributesViaMetaAnnotationWithOverride() throws Exception {
assertTransactionConfigurationAttributes(TransactionConfigurationViaMetaAnnotationWithOverrideTestCase.class,
"overriddenTxMgr", true);
}
@Test
public void isRollbackWithMissingRollback() throws Exception {
assertIsRollback(MissingRollbackTestCase.class, true);
}
@Test
public void isRollbackWithEmptyRollback() throws Exception {
assertIsRollback(EmptyRollbackTestCase.class, true);
}
@Test
public void isRollbackWithExplicitValue() throws Exception {
assertIsRollback(RollbackWithExplicitValueTestCase.class, false);
}
@Test
public void isRollbackViaMetaAnnotation() throws Exception {
assertIsRollback(RollbackViaMetaAnnotationTestCase.class, false);
}
// -------------------------------------------------------------------------
@@ -157,6 +247,13 @@ public class TransactionalTestExecutionListenerTests {
private static @interface MetaTransactional {
}
@Transactional
@Retention(RetentionPolicy.RUNTIME)
private static @interface MetaTxWithOverride {
Propagation propagation() default REQUIRED;
}
@BeforeTransaction
@Retention(RetentionPolicy.RUNTIME)
private static @interface MetaBeforeTransaction {
@@ -167,6 +264,18 @@ public class TransactionalTestExecutionListenerTests {
private static @interface MetaAfterTransaction {
}
@TransactionConfiguration
@Retention(RetentionPolicy.RUNTIME)
private static @interface MetaTxConfig {
String transactionManager() default "metaTxMgr";
}
@Rollback(false)
@Retention(RetentionPolicy.RUNTIME)
private static @interface Commit {
}
private static abstract class Invocable {
boolean invoked = false;
@@ -213,10 +322,6 @@ public class TransactionalTestExecutionListenerTests {
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
static class TransactionalDeclaredOnMethodViaMetaAnnotationTestCase extends Invocable {
@@ -236,6 +341,36 @@ public class TransactionalTestExecutionListenerTests {
}
}
@MetaTxWithOverride(propagation = NOT_SUPPORTED)
static class TransactionalDeclaredOnClassViaMetaAnnotationWithOverrideTestCase extends Invocable {
@BeforeTransaction
public void beforeTransaction() {
invoked = true;
}
public void transactionalTest() {
/* no-op */
}
}
static class TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase extends Invocable {
@BeforeTransaction
public void beforeTransaction() {
invoked = true;
}
@MetaTxWithOverride(propagation = NOT_SUPPORTED)
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
static class BeforeTransactionDeclaredLocallyTestCase extends Invocable {
@BeforeTransaction
@@ -304,4 +439,50 @@ public class TransactionalTestExecutionListenerTests {
}
}
static class MissingTransactionConfigurationTestCase {
}
@TransactionConfiguration
static class EmptyTransactionConfigurationTestCase {
}
@TransactionConfiguration(transactionManager = "tm", defaultRollback = false)
static class TransactionConfigurationWithExplicitValuesTestCase {
}
@MetaTxConfig
static class TransactionConfigurationViaMetaAnnotationTestCase {
}
@MetaTxConfig(transactionManager = "overriddenTxMgr")
static class TransactionConfigurationViaMetaAnnotationWithOverrideTestCase {
}
static class MissingRollbackTestCase {
public void test() {
}
}
static class EmptyRollbackTestCase {
@Rollback
public void test() {
}
}
static class RollbackWithExplicitValueTestCase {
@Rollback(false)
public void test() {
}
}
static class RollbackViaMetaAnnotationTestCase {
@Commit
public void test() {
}
}
}