From a1a87679da99e7c3f199ecbd764a9e712d60f4ac Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Thu, 5 May 2016 19:51:21 +0200 Subject: [PATCH] Support test annotations on interfaces Prior to Java 8 it never really made much sense to author integration tests using interfaces. Consequently, the Spring TestContext Framework has never supported finding test-related annotations on interfaces in its search algorithms. However, Java 8's support for interface default methods introduces new testing use cases for which it makes sense to declare test configuration (e.g., @ContextConfiguration, etc.) on an interface containing default methods instead of on an abstract base class. This commit ensures that all non-repeatable, class-level test annotations in the Spring TestContext Framework can now be declared on test interfaces. The only test annotations that cannot be declared on interfaces are therefore @Sql and @SqlGroup. Issue: SPR-14184 --- .../tests/sample/beans/Employee.java | 9 +- .../test/annotation/Commit.java | 4 +- .../ProfileValueSourceConfiguration.java | 10 +- .../test/annotation/Rollback.java | 2 + .../test/context/ActiveProfiles.java | 4 +- .../test/context/BootstrapUtils.java | 24 ++-- .../test/context/BootstrapWith.java | 6 +- .../test/context/ContextConfiguration.java | 6 +- .../test/context/ContextHierarchy.java | 6 +- .../test/context/TestExecutionListeners.java | 4 +- .../test/context/TestPropertySource.java | 4 +- .../test/util/MetaAnnotationUtils.java | 43 ++++--- .../annotation/ProfileValueUtilsTests.java | 28 ++++- .../context/TestExecutionListenersTests.java | 13 ++ .../cache/ClassLevelDirtiesContextTests.java | 17 +-- .../ActiveProfilesInterfaceTests.java | 65 ++++++++++ .../ActiveProfilesTestInterface.java | 27 ++++ .../BootstrapWithInterfaceTests.java | 43 +++++++ .../BootstrapWithTestInterface.java | 47 +++++++ .../ContextConfigurationInterfaceTests.java | 45 +++++++ .../ContextConfigurationTestInterface.java | 39 ++++++ .../ContextHierarchyInterfaceTests.java | 58 +++++++++ .../ContextHierarchyTestInterface.java | 31 +++++ .../DirtiesContextInterfaceTests.java | 95 ++++++++++++++ .../DirtiesContextTestInterface.java | 27 ++++ .../interfaces/SqlConfigInterfaceTests.java | 46 +++++++ .../interfaces/SqlConfigTestInterface.java | 32 +++++ .../TestPropertySourceInterfaceTests.java | 57 +++++++++ .../TestPropertySourceTestInterface.java | 27 ++++ .../WebAppConfigurationInterfaceTests.java | 44 +++++++ .../WebAppConfigurationTestInterface.java | 37 ++++++ ...lassWithTwoLevelContextHierarchyTests.java | 6 +- ...ansactionalTestExecutionListenerTests.java | 32 +++++ .../test/util/MetaAnnotationUtilsTests.java | 119 +++++++++++++++--- 34 files changed, 973 insertions(+), 84 deletions(-) create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesInterfaceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesTestInterface.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithInterfaceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithTestInterface.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationInterfaceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationTestInterface.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyInterfaceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyTestInterface.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextInterfaceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextTestInterface.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigInterfaceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigTestInterface.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceInterfaceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceTestInterface.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationInterfaceTests.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationTestInterface.java diff --git a/spring-context/src/test/java/org/springframework/tests/sample/beans/Employee.java b/spring-context/src/test/java/org/springframework/tests/sample/beans/Employee.java index 40301c7a24..fc2c639fa5 100644 --- a/spring-context/src/test/java/org/springframework/tests/sample/beans/Employee.java +++ b/spring-context/src/test/java/org/springframework/tests/sample/beans/Employee.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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,6 +20,13 @@ public class Employee extends TestBean { private String co; + public Employee() { + } + + public Employee(String name) { + super(name); + } + public String getCompany() { return co; } diff --git a/spring-test/src/main/java/org/springframework/test/annotation/Commit.java b/spring-test/src/main/java/org/springframework/test/annotation/Commit.java index e23c254fba..98b3d96b0a 100644 --- a/spring-test/src/main/java/org/springframework/test/annotation/Commit.java +++ b/spring-test/src/main/java/org/springframework/test/annotation/Commit.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -18,6 +18,7 @@ package org.springframework.test.annotation; 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; @@ -51,6 +52,7 @@ import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented +@Inherited @Rollback(false) public @interface Commit { } diff --git a/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueSourceConfiguration.java b/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueSourceConfiguration.java index cd28558798..e5f61e45a8 100644 --- a/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueSourceConfiguration.java +++ b/spring-test/src/main/java/org/springframework/test/annotation/ProfileValueSourceConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -24,7 +24,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - *

{@code ProfileValueSourceConfiguration} is a class-level annotation which + * {@code ProfileValueSourceConfiguration} is a class-level annotation which * is used to specify what type of {@link ProfileValueSource} to use when * retrieving profile values configured via the {@link IfProfileValue * @IfProfileValue} annotation. @@ -38,17 +38,15 @@ import java.lang.annotation.Target; * @see IfProfileValue * @see ProfileValueUtils */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) public @interface ProfileValueSourceConfiguration { /** - *

* The type of {@link ProfileValueSource} to use when retrieving * profile values. - *

* * @see SystemProfileValueSource */ diff --git a/spring-test/src/main/java/org/springframework/test/annotation/Rollback.java b/spring-test/src/main/java/org/springframework/test/annotation/Rollback.java index 37bf210279..c1f06e643f 100644 --- a/spring-test/src/main/java/org/springframework/test/annotation/Rollback.java +++ b/spring-test/src/main/java/org/springframework/test/annotation/Rollback.java @@ -18,6 +18,7 @@ package org.springframework.test.annotation; 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; @@ -56,6 +57,7 @@ import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented +@Inherited public @interface Rollback { /** diff --git a/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java b/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java index fc259102ed..54b0889297 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java +++ b/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java @@ -43,10 +43,10 @@ import org.springframework.core.annotation.AliasFor; * @see org.springframework.context.ApplicationContext * @see org.springframework.context.annotation.Profile */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) public @interface ActiveProfiles { /** diff --git a/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java b/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java index 7f26cc2aac..2567a97792 100644 --- a/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java @@ -17,16 +17,15 @@ package org.springframework.test.context; import java.lang.reflect.Constructor; -import java.util.List; +import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeanUtils; import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.util.ClassUtils; -import org.springframework.util.MultiValueMap; /** * {@code BootstrapUtils} is a collection of utility methods to assist with @@ -151,17 +150,16 @@ abstract class BootstrapUtils { * @since 4.3 */ private static Class resolveExplicitTestContextBootstrapper(Class testClass) { - MultiValueMap attributesMultiMap = - AnnotatedElementUtils.getAllAnnotationAttributes(testClass, BootstrapWith.class.getName()); - List values = (attributesMultiMap != null ? attributesMultiMap.get(AnnotationUtils.VALUE) : null); - if (values == null) { + Set annotations = AnnotatedElementUtils.findAllMergedAnnotations(testClass, BootstrapWith.class); + if (annotations.size() < 1) { return null; } - if (values.size() != 1) { - throw new IllegalStateException(String.format("Configuration error: found multiple declarations of " + - "@BootstrapWith on test class [%s] with values %s", testClass.getName(), values)); + if (annotations.size() > 1) { + throw new IllegalStateException(String.format( + "Configuration error: found multiple declarations of @BootstrapWith for test class [%s]: %s", + testClass.getName(), annotations)); } - return (Class) values.get(0); + return annotations.iterator().next().value(); } /** @@ -169,7 +167,9 @@ abstract class BootstrapUtils { */ private static Class resolveDefaultTestContextBootstrapper(Class testClass) throws Exception { ClassLoader classLoader = BootstrapUtils.class.getClassLoader(); - if (AnnotatedElementUtils.isAnnotated(testClass, WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME)) { + AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(testClass, + WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME, false, false); + if (attributes != null) { return ClassUtils.forName(DEFAULT_WEB_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME, classLoader); } return ClassUtils.forName(DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME, classLoader); diff --git a/spring-test/src/main/java/org/springframework/test/context/BootstrapWith.java b/spring-test/src/main/java/org/springframework/test/context/BootstrapWith.java index 302ce42e2a..7cf742f633 100644 --- a/spring-test/src/main/java/org/springframework/test/context/BootstrapWith.java +++ b/spring-test/src/main/java/org/springframework/test/context/BootstrapWith.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -35,10 +35,10 @@ import java.lang.annotation.Target; * @see BootstrapContext * @see TestContextBootstrapper */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) public @interface BootstrapWith { /** diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java index 9087bdbeaf..669adbd65c 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -83,10 +83,10 @@ import org.springframework.core.annotation.AliasFor; * @see MergedContextConfiguration * @see org.springframework.context.ApplicationContext */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) public @interface ContextConfiguration { /** diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextHierarchy.java b/spring-test/src/main/java/org/springframework/test/context/ContextHierarchy.java index 61b2fa8cf6..3a3d285979 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextHierarchy.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextHierarchy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -139,10 +139,10 @@ import java.lang.annotation.Target; * @see ContextConfiguration * @see org.springframework.context.ApplicationContext */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) public @interface ContextHierarchy { /** diff --git a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java index d99c48d7aa..d1db5c75c9 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java @@ -42,10 +42,10 @@ import org.springframework.core.annotation.AliasFor; * @see TestContextManager * @see ContextConfiguration */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) public @interface TestExecutionListeners { /** diff --git a/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java b/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java index ec2528af29..6937870bbf 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java @@ -82,10 +82,10 @@ import org.springframework.core.annotation.AliasFor; * @see org.springframework.core.env.PropertySource * @see org.springframework.context.annotation.PropertySource */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) public @interface TestPropertySource { /** diff --git a/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java b/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java index 913986a440..9c203d743e 100644 --- a/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java +++ b/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java @@ -57,8 +57,8 @@ public abstract class MetaAnnotationUtils { /** * Find the {@link AnnotationDescriptor} for the supplied {@code annotationType} - * on the supplied {@link Class}, traversing its annotations and superclasses - * if no annotation can be found on the given class itself. + * on the supplied {@link Class}, traversing its annotations, interfaces, and + * superclasses if no annotation can be found on the given class itself. *

This method explicitly handles class-level annotations which are not * declared as {@linkplain java.lang.annotation.Inherited inherited} as * well as meta-annotations. @@ -67,14 +67,12 @@ public abstract class MetaAnnotationUtils { *

  • Search for the annotation on the given class and return a corresponding * {@code AnnotationDescriptor} if found. *
  • Recursively search through all annotations that the given class declares. + *
  • Recursively search through all interfaces implemented by the given class. *
  • Recursively search through the superclass hierarchy of the given class. * *

    In this context, the term recursively means that the search - * process continues by returning to step #1 with the current annotation or - * superclass as the class to look for annotations on. - *

    If the supplied {@code clazz} is an interface, only the interface - * itself will be checked; the inheritance hierarchy for interfaces will not - * be traversed. + * process continues by returning to step #1 with the current annotation, + * interface, or superclass as the class to look for annotations on. * @param clazz the class to look for annotations on * @param annotationType the type of annotation to look for * @return the corresponding annotation descriptor if the annotation was found; @@ -123,6 +121,15 @@ public abstract class MetaAnnotationUtils { } } + // Declared on interface? + for (Class ifc : clazz.getInterfaces()) { + AnnotationDescriptor descriptor = findAnnotationDescriptor(ifc, visited, annotationType); + if (descriptor != null) { + return new AnnotationDescriptor(clazz, descriptor.getDeclaringClass(), + descriptor.getComposedAnnotation(), descriptor.getAnnotation()); + } + } + // Declared on a superclass? return findAnnotationDescriptor(clazz.getSuperclass(), visited, annotationType); } @@ -132,8 +139,9 @@ public abstract class MetaAnnotationUtils { * 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}. - *

    This method traverses the annotations and superclasses of the specified - * {@code clazz} if no annotation can be found on the given class itself. + *

    This method traverses the annotations, interfaces, and superclasses + * of the specified {@code clazz} if no annotation can be found on the given + * class itself. *

    This method explicitly handles class-level annotations which are not * declared as {@linkplain java.lang.annotation.Inherited inherited} as * well as meta-annotations. @@ -143,14 +151,12 @@ public abstract class MetaAnnotationUtils { * the given class and return a corresponding {@code UntypedAnnotationDescriptor} * if found. *

  • Recursively search through all annotations that the given class declares. + *
  • Recursively search through all interfaces implemented by the given class. *
  • Recursively search through the superclass hierarchy of the given class. * *

    In this context, the term recursively means that the search - * process continues by returning to step #1 with the current annotation or - * superclass as the class to look for annotations on. - *

    If the supplied {@code clazz} is an interface, only the interface - * itself will be checked; the inheritance hierarchy for interfaces will not - * be traversed. + * process continues by returning to step #1 with the current annotation, + * interface, or superclass as the class to look for annotations on. * @param clazz the class to look for annotations on * @param annotationTypes the types of annotations to look for * @return the corresponding annotation descriptor if one of the annotations @@ -203,6 +209,15 @@ public abstract class MetaAnnotationUtils { } } + // Declared on interface? + for (Class ifc : clazz.getInterfaces()) { + UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes(ifc, visited, annotationTypes); + if (descriptor != null) { + return new UntypedAnnotationDescriptor(clazz, descriptor.getDeclaringClass(), + descriptor.getComposedAnnotation(), descriptor.getAnnotation()); + } + } + // Declared on a superclass? return findAnnotationDescriptorForTypes(clazz.getSuperclass(), visited, annotationTypes); } diff --git a/spring-test/src/test/java/org/springframework/test/annotation/ProfileValueUtilsTests.java b/spring-test/src/test/java/org/springframework/test/annotation/ProfileValueUtilsTests.java index 4296fd077d..da461beec0 100644 --- a/spring-test/src/test/java/org/springframework/test/annotation/ProfileValueUtilsTests.java +++ b/spring-test/src/test/java/org/springframework/test/annotation/ProfileValueUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -91,7 +91,10 @@ public class ProfileValueUtilsTests { assertClassIsEnabled(EnabledAnnotatedMultiValue.class); assertClassIsEnabled(MetaEnabledClass.class); assertClassIsEnabled(MetaEnabledWithCustomProfileValueSourceClass.class); + assertClassIsEnabled(EnabledWithCustomProfileValueSourceOnTestInterface.class); + assertClassIsDisabled(DisabledAnnotatedSingleValue.class); + assertClassIsDisabled(DisabledAnnotatedSingleValueOnTestInterface.class); assertClassIsDisabled(DisabledAnnotatedMultiValue.class); assertClassIsDisabled(MetaDisabledClass.class); assertClassIsDisabled(MetaDisabledWithCustomProfileValueSourceClass.class); @@ -105,6 +108,7 @@ public class ProfileValueUtilsTests { assertMethodIsEnabled(ENABLED_ANNOTATED_METHOD, EnabledAnnotatedSingleValue.class); assertMethodIsDisabled(DISABLED_ANNOTATED_METHOD, EnabledAnnotatedSingleValue.class); + assertMethodIsEnabled(NON_ANNOTATED_METHOD, MetaEnabledAnnotatedSingleValue.class); assertMethodIsEnabled(ENABLED_ANNOTATED_METHOD, MetaEnabledAnnotatedSingleValue.class); assertMethodIsDisabled(DISABLED_ANNOTATED_METHOD, MetaEnabledAnnotatedSingleValue.class); @@ -117,6 +121,8 @@ public class ProfileValueUtilsTests { assertMethodIsDisabled(ENABLED_ANNOTATED_METHOD, DisabledAnnotatedSingleValue.class); assertMethodIsDisabled(DISABLED_ANNOTATED_METHOD, DisabledAnnotatedSingleValue.class); + assertMethodIsDisabled(NON_ANNOTATED_METHOD, DisabledAnnotatedSingleValueOnTestInterface.class); + assertMethodIsDisabled(NON_ANNOTATED_METHOD, MetaDisabledAnnotatedSingleValue.class); assertMethodIsDisabled(ENABLED_ANNOTATED_METHOD, MetaDisabledAnnotatedSingleValue.class); assertMethodIsDisabled(DISABLED_ANNOTATED_METHOD, MetaDisabledAnnotatedSingleValue.class); @@ -176,6 +182,17 @@ public class ProfileValueUtilsTests { } } + @IfProfileValue(name = NAME, value = VALUE + "X") + private interface IfProfileValueTestInterface { + } + + @SuppressWarnings("unused") + private static class DisabledAnnotatedSingleValueOnTestInterface implements IfProfileValueTestInterface { + + public void nonAnnotatedMethod() { + } + } + @SuppressWarnings("unused") @IfProfileValue(name = NAME, values = { "foo", VALUE, "bar" }) private static class EnabledAnnotatedMultiValue { @@ -302,4 +319,13 @@ public class ProfileValueUtilsTests { private static class MetaDisabledWithCustomProfileValueSourceClass { } + @ProfileValueSourceConfiguration(HardCodedProfileValueSource.class) + private interface CustomProfileValueSourceTestInterface { + } + + @IfProfileValue(name = NAME, value = "42") + private static class EnabledWithCustomProfileValueSourceOnTestInterface + implements CustomProfileValueSourceTestInterface { + } + } diff --git a/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java b/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java index 648655fb9c..4df53d82ff 100644 --- a/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java @@ -115,6 +115,12 @@ public class TestExecutionListenersTests { assertNumRegisteredListeners(ExplicitListenersTestCase.class, 3); } + @Test + public void customListenersDeclaredOnInterface() { + assertRegisteredListeners(ExplicitListenersOnTestInterfaceTestCase.class, + asList(FooTestExecutionListener.class, BarTestExecutionListener.class)); + } + @Test public void nonInheritedListeners() { assertNumRegisteredListeners(NonInheritedListenersTestCase.class, 1); @@ -229,6 +235,13 @@ public class TestExecutionListenersTests { static class NonInheritedListenersTestCase extends InheritedListenersTestCase { } + @TestExecutionListeners({ FooTestExecutionListener.class, BarTestExecutionListener.class }) + interface ExplicitListenersTestInterface { + } + + static class ExplicitListenersOnTestInterfaceTestCase implements ExplicitListenersTestInterface { + } + @TestExecutionListeners(listeners = FooTestExecutionListener.class, value = BarTestExecutionListener.class) static class DuplicateListenersConfigTestCase { } diff --git a/spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTests.java b/spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTests.java index 896c4654a8..9da13e7bcf 100644 --- a/spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/cache/ClassLevelDirtiesContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -30,10 +30,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestExecutionListeners; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; -import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.test.context.junit4.SpringRunner; import static org.junit.Assert.*; import static org.springframework.test.context.cache.ContextCacheTestUtils.*; @@ -41,9 +38,8 @@ import static org.springframework.test.context.junit4.JUnitTestingUtils.*; /** * JUnit 4 based integration test which verifies correct {@linkplain ContextCache - * application context caching} in conjunction with the - * {@link SpringJUnit4ClassRunner} and {@link DirtiesContext @DirtiesContext} - * at the class level. + * application context caching} in conjunction with the {@link SpringRunner} and + * {@link DirtiesContext @DirtiesContext} at the class level. * * @author Sam Brannen * @since 3.0 @@ -148,10 +144,9 @@ public class ClassLevelDirtiesContextTests { // ------------------------------------------------------------------- - @RunWith(SpringJUnit4ClassRunner.class) - @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class }) + @RunWith(SpringRunner.class) @ContextConfiguration - public static abstract class BaseTestCase { + static abstract class BaseTestCase { @Configuration static class Config { diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesInterfaceTests.java new file mode 100644 index 0000000000..80d33c8d1a --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesInterfaceTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-2016 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.interfaces; + +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.context.annotation.Profile; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.tests.sample.beans.Employee; + +import static org.junit.Assert.*; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@RunWith(SpringRunner.class) +public class ActiveProfilesInterfaceTests implements ActiveProfilesTestInterface { + + @Autowired + Employee employee; + + + @Test + public void profileFromTestInterface() { + assertNotNull(employee); + assertEquals("dev", employee.getName()); + } + + + @Configuration + static class Config { + + @Bean + @Profile("dev") + Employee employee1() { + return new Employee("dev"); + } + + @Bean + @Profile("prod") + Employee employee2() { + return new Employee("prod"); + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesTestInterface.java new file mode 100644 index 0000000000..63ef2004f9 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ActiveProfilesTestInterface.java @@ -0,0 +1,27 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import org.springframework.test.context.ActiveProfiles; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@ActiveProfiles("dev") +interface ActiveProfilesTestInterface { +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithInterfaceTests.java new file mode 100644 index 0000000000..47e52b9ffd --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithInterfaceTests.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.*; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@RunWith(SpringRunner.class) +public class BootstrapWithInterfaceTests implements BootstrapWithTestInterface { + + @Autowired + String foo; + + + @Test + public void injectedBean() { + assertEquals("foo", foo); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithTestInterface.java new file mode 100644 index 0000000000..993f3afaed --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/BootstrapWithTestInterface.java @@ -0,0 +1,47 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import java.util.List; + +import org.springframework.test.context.BootstrapWith; +import org.springframework.test.context.ContextCustomizer; +import org.springframework.test.context.ContextCustomizerFactory; +import org.springframework.test.context.configuration.interfaces.BootstrapWithTestInterface.CustomTestContextBootstrapper; +import org.springframework.test.context.support.DefaultTestContextBootstrapper; + +import static java.util.Collections.*; + +/** + * @author Sam Brannen + * @author Phillip Webb + * @since 4.3 + */ +@BootstrapWith(CustomTestContextBootstrapper.class) +interface BootstrapWithTestInterface { + + static class CustomTestContextBootstrapper extends DefaultTestContextBootstrapper { + + @Override + protected List getContextCustomizerFactories() { + return singletonList( + (ContextCustomizerFactory) (testClass, configAttributes) -> (ContextCustomizer) (context, + mergedConfig) -> context.getBeanFactory().registerSingleton("foo", "foo")); + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationInterfaceTests.java new file mode 100644 index 0000000000..4a035e77a0 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationInterfaceTests.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.tests.sample.beans.Employee; + +import static org.junit.Assert.*; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@RunWith(SpringRunner.class) +public class ContextConfigurationInterfaceTests implements ContextConfigurationTestInterface { + + @Autowired + Employee employee; + + + @Test + public void profileFromTestInterface() { + assertNotNull(employee); + assertEquals("Dilbert", employee.getName()); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationTestInterface.java new file mode 100644 index 0000000000..7eb452fcec --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextConfigurationTestInterface.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.configuration.interfaces.ContextConfigurationTestInterface.Config; +import org.springframework.tests.sample.beans.Employee; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@ContextConfiguration(classes = Config.class) +interface ContextConfigurationTestInterface { + + static class Config { + + @Bean + Employee employee() { + return new Employee("Dilbert"); + } + } + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyInterfaceTests.java new file mode 100644 index 0000000000..832c244b79 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyInterfaceTests.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2016 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.interfaces; + +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.junit4.SpringRunner; + +import static org.junit.Assert.*; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@RunWith(SpringRunner.class) +public class ContextHierarchyInterfaceTests implements ContextHierarchyTestInterface { + + @Autowired + String foo; + + @Autowired + String bar; + + @Autowired + String baz; + + @Autowired + 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); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyTestInterface.java new file mode 100644 index 0000000000..538e2a932d --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/ContextHierarchyTestInterface.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.ContextHierarchy; +import org.springframework.test.context.hierarchies.standard.SingleTestClassWithTwoLevelContextHierarchyTests; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@ContextHierarchy({ + @ContextConfiguration(classes = SingleTestClassWithTwoLevelContextHierarchyTests.ParentConfig.class), + @ContextConfiguration(classes = SingleTestClassWithTwoLevelContextHierarchyTests.ChildConfig.class) }) +interface ContextHierarchyTestInterface { +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextInterfaceTests.java new file mode 100644 index 0000000000..6b0be0a151 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextInterfaceTests.java @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.*; +import static org.springframework.test.context.cache.ContextCacheTestUtils.*; +import static org.springframework.test.context.junit4.JUnitTestingUtils.*; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@RunWith(JUnit4.class) +public class DirtiesContextInterfaceTests { + + private static final AtomicInteger cacheHits = new AtomicInteger(0); + private static final AtomicInteger cacheMisses = new AtomicInteger(0); + + + @BeforeClass + public static void verifyInitialCacheState() { + resetContextCache(); + // Reset static counters in case tests are run multiple times in a test suite -- + // for example, via JUnit's @Suite. + cacheHits.set(0); + cacheMisses.set(0); + assertContextCacheStatistics("BeforeClass", 0, cacheHits.get(), cacheMisses.get()); + } + + @AfterClass + public static void verifyFinalCacheState() { + assertContextCacheStatistics("AfterClass", 0, cacheHits.get(), cacheMisses.get()); + } + + @Test + public void verifyDirtiesContextBehavior() throws Exception { + runTestClassAndAssertStats(ClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase.class, 1); + assertContextCacheStatistics("after class-level @DirtiesContext with clean test method and default class mode", + 0, cacheHits.get(), cacheMisses.incrementAndGet()); + } + + private void runTestClassAndAssertStats(Class testClass, int expectedTestCount) throws Exception { + runTestsAndAssertCounters(testClass, expectedTestCount, 0, expectedTestCount, 0, 0); + } + + + @RunWith(SpringRunner.class) + public static class ClassLevelDirtiesContextWithCleanMethodsAndDefaultModeTestCase + implements DirtiesContextTestInterface { + + @Autowired + ApplicationContext applicationContext; + + + @Test + public void verifyContextWasAutowired() { + assertNotNull("The application context should have been autowired.", this.applicationContext); + } + + + @Configuration + static class Config { + /* no beans */ + } + + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextTestInterface.java new file mode 100644 index 0000000000..cd486e14e7 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/DirtiesContextTestInterface.java @@ -0,0 +1,27 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import org.springframework.test.annotation.DirtiesContext; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@DirtiesContext +interface DirtiesContextTestInterface { +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigInterfaceTests.java new file mode 100644 index 0000000000..26f044f81b --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigInterfaceTests.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import org.junit.Test; + +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; +import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; + +import static org.junit.Assert.*; + +/** + * @author Sam Brannen + * @since 4.3 + */ +public class SqlConfigInterfaceTests extends AbstractTransactionalJUnit4SpringContextTests + implements SqlConfigTestInterface { + + @Test + @Sql(scripts = "/org/springframework/test/context/jdbc/schema.sql", // + config = @SqlConfig(separator = ";")) + @Sql("/org/springframework/test/context/jdbc/data-add-users-with-custom-script-syntax.sql") + public void methodLevelScripts() { + assertNumUsers(3); + } + + protected void assertNumUsers(int expected) { + assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user")); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigTestInterface.java new file mode 100644 index 0000000000..a707a4c2c0 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/SqlConfigTestInterface.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.jdbc.EmptyDatabaseConfig; +import org.springframework.test.context.jdbc.SqlConfig; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@ContextConfiguration(classes = EmptyDatabaseConfig.class) +@DirtiesContext +@SqlConfig(commentPrefix = "`", blockCommentStartDelimiter = "#$", blockCommentEndDelimiter = "$#", separator = "@@") +interface SqlConfigTestInterface { +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceInterfaceTests.java new file mode 100644 index 0000000000..66dd6057c2 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceInterfaceTests.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@RunWith(SpringRunner.class) +public class TestPropertySourceInterfaceTests implements TestPropertySourceTestInterface { + + @Autowired + Environment env; + + + @Test + public void propertiesAreAvailableInEnvironment() { + assertThat(property("foo"), is("bar")); + assertThat(property("enigma"), is("42")); + } + + private String property(String key) { + return env.getProperty(key); + } + + + @Configuration + static class Config { + /* no user beans required for these tests */ + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceTestInterface.java new file mode 100644 index 0000000000..af27989e08 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/TestPropertySourceTestInterface.java @@ -0,0 +1,27 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import org.springframework.test.context.TestPropertySource; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@TestPropertySource(properties = { "foo = bar", "enigma: 42" }) +interface TestPropertySourceTestInterface { +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationInterfaceTests.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationInterfaceTests.java new file mode 100644 index 0000000000..fdb6706be7 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationInterfaceTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.context.WebApplicationContext; + +import static org.junit.Assert.*; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@RunWith(SpringRunner.class) +public class WebAppConfigurationInterfaceTests implements WebAppConfigurationTestInterface { + + @Autowired + WebApplicationContext wac; + + + @Test + public void wacLoaded() { + assertNotNull(wac); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationTestInterface.java b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationTestInterface.java new file mode 100644 index 0000000000..dcfabd14cb --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/configuration/interfaces/WebAppConfigurationTestInterface.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2016 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.interfaces; + +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.configuration.interfaces.WebAppConfigurationTestInterface.Config; +import org.springframework.test.context.web.WebAppConfiguration; + +/** + * @author Sam Brannen + * @since 4.3 + */ +@WebAppConfiguration +@ContextConfiguration(classes = Config.class) +interface WebAppConfigurationTestInterface { + + @Configuration + static class Config { + /* no user beans required for these tests */ + } + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/hierarchies/standard/SingleTestClassWithTwoLevelContextHierarchyTests.java b/spring-test/src/test/java/org/springframework/test/context/hierarchies/standard/SingleTestClassWithTwoLevelContextHierarchyTests.java index 2868b6944e..f8ba83fd2b 100644 --- a/spring-test/src/test/java/org/springframework/test/context/hierarchies/standard/SingleTestClassWithTwoLevelContextHierarchyTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/hierarchies/standard/SingleTestClassWithTwoLevelContextHierarchyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -40,7 +40,7 @@ import static org.junit.Assert.*; public class SingleTestClassWithTwoLevelContextHierarchyTests { @Configuration - static class ParentConfig { + public static class ParentConfig { @Bean public String foo() { @@ -54,7 +54,7 @@ public class SingleTestClassWithTwoLevelContextHierarchyTests { } @Configuration - static class ChildConfig { + public static class ChildConfig { @Bean public String bar() { diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java index 0e4f7276f8..82cb00816e 100644 --- a/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java @@ -54,6 +54,7 @@ public class TransactionalTestExecutionListenerTests { private final TransactionalTestExecutionListener listener = new TransactionalTestExecutionListener() { + @Override protected PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) { return tm; } @@ -353,6 +354,16 @@ public class TransactionalTestExecutionListenerTests { assertIsRollback(ClassLevelRollbackViaMetaAnnotationTestCase.class, false); } + @Test + public void isRollbackWithClassLevelRollbackWithExplicitValueOnTestInterface() throws Exception { + assertIsRollback(ClassLevelRollbackWithExplicitValueOnTestInterfaceTestCase.class, false); + } + + @Test + public void isRollbackWithClassLevelRollbackViaMetaAnnotationOnTestInterface() throws Exception { + assertIsRollback(ClassLevelRollbackViaMetaAnnotationOnTestInterfaceTestCase.class, false); + } + @Test public void isRollbackWithRollbackAndTransactionConfigurationDeclaredAtClassLevel() throws Exception { Class clazz = ClassLevelRollbackAndTransactionConfigurationTestCase.class; @@ -712,4 +723,25 @@ public class TransactionalTestExecutionListenerTests { } } + @Rollback(false) + interface RollbackFalseTestInterface { + } + + static class ClassLevelRollbackWithExplicitValueOnTestInterfaceTestCase implements RollbackFalseTestInterface { + + public void test() { + } + } + + @Commit + interface RollbackFalseViaMetaAnnotationTestInterface { + } + + static class ClassLevelRollbackViaMetaAnnotationOnTestInterfaceTestCase + implements RollbackFalseViaMetaAnnotationTestInterface { + + public void test() { + } + } + } diff --git a/spring-test/src/test/java/org/springframework/test/util/MetaAnnotationUtilsTests.java b/spring-test/src/test/java/org/springframework/test/util/MetaAnnotationUtilsTests.java index 03db1d6049..3416f215f0 100644 --- a/spring-test/src/test/java/org/springframework/test/util/MetaAnnotationUtilsTests.java +++ b/spring-test/src/test/java/org/springframework/test/util/MetaAnnotationUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -25,10 +25,13 @@ import java.lang.annotation.Target; import org.junit.Test; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor; +import org.springframework.test.util.MetaAnnotationUtils.UntypedAnnotationDescriptor; import org.springframework.transaction.annotation.Transactional; import static org.junit.Assert.*; @@ -112,10 +115,27 @@ public class MetaAnnotationUtilsTests { @Test public void findAnnotationDescriptorWithInheritedAnnotationOnInterface() throws Exception { // Note: @Transactional is inherited - assertEquals(InheritedAnnotationInterface.class, - findAnnotationDescriptor(InheritedAnnotationInterface.class, Transactional.class).getRootDeclaringClass()); - assertNull(findAnnotationDescriptor(SubInheritedAnnotationInterface.class, Transactional.class)); - assertNull(findAnnotationDescriptor(SubSubInheritedAnnotationInterface.class, Transactional.class)); + Transactional rawAnnotation = InheritedAnnotationInterface.class.getAnnotation(Transactional.class); + + AnnotationDescriptor descriptor; + + descriptor = findAnnotationDescriptor(InheritedAnnotationInterface.class, Transactional.class); + assertNotNull(descriptor); + assertEquals(InheritedAnnotationInterface.class, descriptor.getRootDeclaringClass()); + assertEquals(InheritedAnnotationInterface.class, descriptor.getDeclaringClass()); + assertEquals(rawAnnotation, descriptor.getAnnotation()); + + descriptor = findAnnotationDescriptor(SubInheritedAnnotationInterface.class, Transactional.class); + assertNotNull(descriptor); + assertEquals(SubInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass()); + assertEquals(InheritedAnnotationInterface.class, descriptor.getDeclaringClass()); + assertEquals(rawAnnotation, descriptor.getAnnotation()); + + descriptor = findAnnotationDescriptor(SubSubInheritedAnnotationInterface.class, Transactional.class); + assertNotNull(descriptor); + assertEquals(SubSubInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass()); + assertEquals(InheritedAnnotationInterface.class, descriptor.getDeclaringClass()); + assertEquals(rawAnnotation, descriptor.getAnnotation()); } @Test @@ -130,9 +150,21 @@ public class MetaAnnotationUtilsTests { @Test public void findAnnotationDescriptorForNonInheritedAnnotationOnInterface() throws Exception { // Note: @Order is not inherited. - assertEquals(NonInheritedAnnotationInterface.class, - findAnnotationDescriptor(NonInheritedAnnotationInterface.class, Order.class).getRootDeclaringClass()); - assertNull(findAnnotationDescriptor(SubNonInheritedAnnotationInterface.class, Order.class)); + Order rawAnnotation = NonInheritedAnnotationInterface.class.getAnnotation(Order.class); + + AnnotationDescriptor descriptor; + + descriptor = findAnnotationDescriptor(NonInheritedAnnotationInterface.class, Order.class); + assertNotNull(descriptor); + assertEquals(NonInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass()); + assertEquals(NonInheritedAnnotationInterface.class, descriptor.getDeclaringClass()); + assertEquals(rawAnnotation, descriptor.getAnnotation()); + + descriptor = findAnnotationDescriptor(SubNonInheritedAnnotationInterface.class, Order.class); + assertNotNull(descriptor); + assertEquals(SubNonInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass()); + assertEquals(NonInheritedAnnotationInterface.class, descriptor.getDeclaringClass()); + assertEquals(rawAnnotation, descriptor.getAnnotation()); } @Test @@ -158,7 +190,17 @@ public class MetaAnnotationUtilsTests { @Test public void findAnnotationDescriptorForClassWithMetaAnnotatedInterface() { - assertNull(findAnnotationDescriptor(ClassWithMetaAnnotatedInterface.class, Component.class)); + Component rawAnnotation = AnnotationUtils.findAnnotation(ClassWithMetaAnnotatedInterface.class, + Component.class); + + AnnotationDescriptor descriptor; + + descriptor = findAnnotationDescriptor(ClassWithMetaAnnotatedInterface.class, Component.class); + assertNotNull(descriptor); + assertEquals(ClassWithMetaAnnotatedInterface.class, descriptor.getRootDeclaringClass()); + assertEquals(Meta1.class, descriptor.getDeclaringClass()); + assertEquals(rawAnnotation, descriptor.getAnnotation()); + assertEquals(Meta1.class, descriptor.getComposedAnnotation().annotationType()); } @Test @@ -253,11 +295,27 @@ public class MetaAnnotationUtilsTests { @SuppressWarnings("unchecked") public void findAnnotationDescriptorForTypesWithInheritedAnnotationOnInterface() throws Exception { // Note: @Transactional is inherited - assertEquals( - InheritedAnnotationInterface.class, - findAnnotationDescriptorForTypes(InheritedAnnotationInterface.class, Transactional.class).getRootDeclaringClass()); - assertNull(findAnnotationDescriptorForTypes(SubInheritedAnnotationInterface.class, Transactional.class)); - assertNull(findAnnotationDescriptorForTypes(SubSubInheritedAnnotationInterface.class, Transactional.class)); + Transactional rawAnnotation = InheritedAnnotationInterface.class.getAnnotation(Transactional.class); + + UntypedAnnotationDescriptor descriptor; + + descriptor = findAnnotationDescriptorForTypes(InheritedAnnotationInterface.class, Transactional.class); + assertNotNull(descriptor); + assertEquals(InheritedAnnotationInterface.class, descriptor.getRootDeclaringClass()); + assertEquals(InheritedAnnotationInterface.class, descriptor.getDeclaringClass()); + assertEquals(rawAnnotation, descriptor.getAnnotation()); + + descriptor = findAnnotationDescriptorForTypes(SubInheritedAnnotationInterface.class, Transactional.class); + assertNotNull(descriptor); + assertEquals(SubInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass()); + assertEquals(InheritedAnnotationInterface.class, descriptor.getDeclaringClass()); + assertEquals(rawAnnotation, descriptor.getAnnotation()); + + descriptor = findAnnotationDescriptorForTypes(SubSubInheritedAnnotationInterface.class, Transactional.class); + assertNotNull(descriptor); + assertEquals(SubSubInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass()); + assertEquals(InheritedAnnotationInterface.class, descriptor.getDeclaringClass()); + assertEquals(rawAnnotation, descriptor.getAnnotation()); } @Test @@ -274,10 +332,21 @@ public class MetaAnnotationUtilsTests { @SuppressWarnings("unchecked") public void findAnnotationDescriptorForTypesForNonInheritedAnnotationOnInterface() throws Exception { // Note: @Order is not inherited. - assertEquals( - NonInheritedAnnotationInterface.class, - findAnnotationDescriptorForTypes(NonInheritedAnnotationInterface.class, Order.class).getRootDeclaringClass()); - assertNull(findAnnotationDescriptorForTypes(SubNonInheritedAnnotationInterface.class, Order.class)); + Order rawAnnotation = NonInheritedAnnotationInterface.class.getAnnotation(Order.class); + + UntypedAnnotationDescriptor descriptor; + + descriptor = findAnnotationDescriptorForTypes(NonInheritedAnnotationInterface.class, Order.class); + assertNotNull(descriptor); + assertEquals(NonInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass()); + assertEquals(NonInheritedAnnotationInterface.class, descriptor.getDeclaringClass()); + assertEquals(rawAnnotation, descriptor.getAnnotation()); + + descriptor = findAnnotationDescriptorForTypes(SubNonInheritedAnnotationInterface.class, Order.class); + assertNotNull(descriptor); + assertEquals(SubNonInheritedAnnotationInterface.class, descriptor.getRootDeclaringClass()); + assertEquals(NonInheritedAnnotationInterface.class, descriptor.getDeclaringClass()); + assertEquals(rawAnnotation, descriptor.getAnnotation()); } @Test @@ -345,8 +414,18 @@ public class MetaAnnotationUtilsTests { @Test @SuppressWarnings("unchecked") public void findAnnotationDescriptorForTypesForClassWithMetaAnnotatedInterface() { - assertNull(findAnnotationDescriptorForTypes(ClassWithMetaAnnotatedInterface.class, Service.class, - Component.class, Order.class, Transactional.class)); + Component rawAnnotation = AnnotationUtils.findAnnotation(ClassWithMetaAnnotatedInterface.class, + Component.class); + + UntypedAnnotationDescriptor descriptor; + + descriptor = findAnnotationDescriptorForTypes(ClassWithMetaAnnotatedInterface.class, Service.class, + Component.class, Order.class, Transactional.class); + assertNotNull(descriptor); + assertEquals(ClassWithMetaAnnotatedInterface.class, descriptor.getRootDeclaringClass()); + assertEquals(Meta1.class, descriptor.getDeclaringClass()); + assertEquals(rawAnnotation, descriptor.getAnnotation()); + assertEquals(Meta1.class, descriptor.getComposedAnnotation().annotationType()); } @Test