diff --git a/spring-test/src/main/java/org/springframework/test/context/TestConstructor.java b/spring-test/src/main/java/org/springframework/test/context/TestConstructor.java
index 461eaa4e299..60a5a34a669 100644
--- a/spring-test/src/main/java/org/springframework/test/context/TestConstructor.java
+++ b/spring-test/src/main/java/org/springframework/test/context/TestConstructor.java
@@ -25,14 +25,14 @@ import java.lang.annotation.Target;
/**
* {@code @TestConstructor} is a type-level annotation that is used to configure
- * whether a test class constructor should be automatically autowired from
- * components in the test's {@link org.springframework.context.ApplicationContext
+ * how the parameters of a test class constructor are autowired from components
+ * in the test's {@link org.springframework.context.ApplicationContext
* ApplicationContext}.
*
*
If {@code @TestConstructor} is not present or meta-present
- * on a test class, the default test constructor autowire mode will be used.
- * See {@link #TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME} for details on how to change
- * the default mode. Note, however, that a local declaration of
+ * on a test class, the default test constructor autowire mode will be
+ * used. See {@link #TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME} for details on
+ * how to change the default mode. Note, however, that a local declaration of
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired} on
* a constructor takes precedence over both {@code @TestConstructor} and the default
* mode.
@@ -40,8 +40,8 @@ import java.lang.annotation.Target;
*
This annotation may be used as a meta-annotation to create custom
* composed annotations.
*
- *
As of Spring Framework 5.2, this annotation is only supported in conjunction with
- * the {@link org.springframework.test.context.junit.jupiter.SpringExtension
+ *
As of Spring Framework 5.2, this annotation is only supported in conjunction
+ * with the {@link org.springframework.test.context.junit.jupiter.SpringExtension
* SpringExtension} for use with JUnit Jupiter. Note that the {@code SpringExtension} is
* often automatically registered for you — for example, when using annotations such as
* {@link org.springframework.test.context.junit.jupiter.SpringJUnitConfig @SpringJUnitConfig} and
@@ -66,29 +66,64 @@ import java.lang.annotation.Target;
public @interface TestConstructor {
/**
- * System property used to configure the default test constructor autowire
- * mode: {@value #TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME}.
+ * JVM system property used to change the default test constructor
+ * autowire mode: {@value #TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME}.
+ *
Acceptable values include enum constants defined in {@link AutowireMode},
+ * ignoring case. For example, the default may be changed to {@link AutowireMode#ALL}
+ * by supplying the following JVM system property via the command line.
+ *
-Dspring.test.constructor.autowire.mode=all
+ * If the property is not set to {@code ALL}, parameters for test class
+ * constructors will be autowired according to {@link AutowireMode#ANNOTATED}
+ * semantics by default.
*
May alternatively be configured via the
- * {@link org.springframework.core.SpringProperties SpringProperties} mechanism.
- *
If the property is not set, test class constructors will not be automatically
- * autowired.
- * @see #autowire
+ * {@link org.springframework.core.SpringProperties SpringProperties}
+ * mechanism.
+ * @see #autowireMode
*/
- String TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME = "spring.test.constructor.autowire";
+ String TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME = "spring.test.constructor.autowire.mode";
/**
- * Flag for setting the test constructor autowire mode for the
- * current test class.
+ * Flag for setting the test constructor {@linkplain AutowireMode autowire
+ * mode} for the current test class.
*
Setting this flag overrides the global default. See
- * {@link #TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME} for details on how to
- * change the global default.
- * @return {@code true} if all test constructor arguments should be autowired
- * from the test's {@link org.springframework.context.ApplicationContext
- * ApplicationContext}
- * @see #TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME
+ * {@link #TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME} for details on how
+ * to change the global default.
+ * @return an {@link AutowireMode} to take precedence over the global default
+ * @see #TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME
* @see org.springframework.beans.factory.annotation.Autowired @Autowired
+ * @see AutowireMode#ALL
+ * @see AutowireMode#ANNOTATED
*/
- boolean autowire();
+ AutowireMode autowireMode();
+
+
+ /**
+ * Defines autowiring modes for parameters in a test constructor.
+ * @see #ALL
+ * @see #ANNOTATED
+ */
+ enum AutowireMode {
+
+ /**
+ * All test constructor parameters will be autowired as if the constructor
+ * itself were annotated with
+ * {@link org.springframework.beans.factory.annotation.Autowired @Autowired}.
+ * @see #ANNOTATED
+ */
+ ALL,
+
+ /**
+ * Each individual test constructor parameter will only be autowired if it
+ * is annotated with
+ * {@link org.springframework.beans.factory.annotation.Autowired @Autowired},
+ * {@link org.springframework.beans.factory.annotation.Qualifier @Qualifier},
+ * or {@link org.springframework.beans.factory.annotation.Value @Value},
+ * or if the constructor itself is annotated with {@code @Autowired}.
+ * @see #ALL
+ */
+ ANNOTATED;
+
+ }
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/TestConstructorUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/TestConstructorUtils.java
index d946033676e..a11e8afb7a2 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/TestConstructorUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/TestConstructorUtils.java
@@ -19,10 +19,14 @@ package org.springframework.test.context.support;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.SpringProperties;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.test.context.TestConstructor;
+import org.springframework.test.context.TestConstructor.AutowireMode;
/**
* Utility methods for working with {@link TestConstructor @TestConstructor}.
@@ -35,10 +39,12 @@ import org.springframework.test.context.TestConstructor;
*/
public abstract class TestConstructorUtils {
+ private static final Log logger = LogFactory.getLog(TestConstructorUtils.class);
+
+
private TestConstructorUtils() {
}
-
/**
* Determine if the supplied executable for the given test class is an
* autowirable constructor.
@@ -67,9 +73,11 @@ public abstract class TestConstructorUtils {
*
The constructor is annotated with {@link Autowired @Autowired}.
* {@link TestConstructor @TestConstructor} is present or
* meta-present on the test class with
- * {@link TestConstructor#autowire autowire} set to {@code true}.
- * The default test constructor autowire mode is set to {@code true}
- * (see {@link TestConstructor#TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME}).
+ * {@link TestConstructor#autowireMode() autowireMode} set to
+ * {@link AutowireMode#ALL ALL}.
+ * The default test constructor autowire mode has been changed
+ * to {@code ALL} (see
+ * {@link TestConstructor#TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME}).
*
*
* @param constructor a constructor for the test class
@@ -82,13 +90,32 @@ public abstract class TestConstructorUtils {
if (AnnotatedElementUtils.hasAnnotation(constructor, Autowired.class)) {
return true;
}
+
+ // Default to Autowire.ANNOTATED semantics.
+ AutowireMode autowireMode = AutowireMode.ANNOTATED;
+
// Is the test class annotated with @TestConstructor?
TestConstructor testConstructor = AnnotatedElementUtils.findMergedAnnotation(testClass, TestConstructor.class);
if (testConstructor != null) {
- return testConstructor.autowire();
+ autowireMode = testConstructor.autowireMode();
}
- // Else use global default.
- return SpringProperties.getFlag(TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME);
+ else {
+ // Custom global default?
+ String value = SpringProperties.getProperty(TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME);
+ if (value != null) {
+ try {
+ autowireMode = AutowireMode.valueOf(value.trim().toUpperCase());
+ }
+ catch (Exception ex) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Failed to parse autowire mode '%s' for property '%s': %s", value,
+ TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME, ex.getMessage()));
+ }
+ }
+ }
+ }
+
+ return (autowireMode == AutowireMode.ALL);
}
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/TestConstructorAnnotationIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/TestConstructorAnnotationIntegrationTests.java
index 6c5f12bf74d..7faa8450274 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/TestConstructorAnnotationIntegrationTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/TestConstructorAnnotationIntegrationTests.java
@@ -28,6 +28,7 @@ import org.springframework.test.context.junit.jupiter.comics.Dog;
import org.springframework.test.context.junit.jupiter.comics.Person;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.test.context.TestConstructor.AutowireMode.ALL;
/**
* Integration tests which demonstrate support for automatically
@@ -45,7 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
@SpringJUnitConfig(TestConfig.class)
@TestPropertySource(properties = "enigma = 42")
-@TestConstructor(autowire = true)
+@TestConstructor(autowireMode = ALL)
class TestConstructorAnnotationIntegrationTests {
final ApplicationContext applicationContext;
@@ -53,6 +54,7 @@ class TestConstructorAnnotationIntegrationTests {
final Dog dog;
final Integer enigma;
+
TestConstructorAnnotationIntegrationTests(ApplicationContext applicationContext, Person dilbert, Dog dog,
@Value("${enigma}") Integer enigma) {
diff --git a/spring-test/src/test/java/org/springframework/test/context/support/TestConstructorUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/support/TestConstructorUtilsTests.java
index 8904ad18611..e1d8ee60049 100644
--- a/spring-test/src/test/java/org/springframework/test/context/support/TestConstructorUtilsTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/support/TestConstructorUtilsTests.java
@@ -22,9 +22,13 @@ import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.SpringProperties;
import org.springframework.test.context.TestConstructor;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.test.context.TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME;
+import static org.springframework.test.context.TestConstructor.AutowireMode.ALL;
+import static org.springframework.test.context.TestConstructor.AutowireMode.ANNOTATED;
/**
* Unit tests for {@link TestConstructorUtils}.
@@ -36,7 +40,7 @@ public class TestConstructorUtilsTests {
@After
public void clearGlobalFlag() {
- System.clearProperty(TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME);
+ setGlobalFlag(null);
}
@Test
@@ -66,6 +70,26 @@ public class TestConstructorUtilsTests {
assertNotAutowirable(TestConstructorAnnotationOverridesGlobalFlagTestCase.class);
}
+ @Test
+ public void globalFlagVariations() throws Exception {
+ Class> testClass = AutomaticallyAutowiredTestCase.class;
+
+ setGlobalFlag(ALL.name());
+ assertAutowirable(testClass);
+
+ setGlobalFlag(ALL.name().toLowerCase());
+ assertAutowirable(testClass);
+
+ setGlobalFlag("\t" + ALL.name().toLowerCase() + " ");
+ assertAutowirable(testClass);
+
+ setGlobalFlag("bogus");
+ assertNotAutowirable(testClass);
+
+ setGlobalFlag(" ");
+ assertNotAutowirable(testClass);
+ }
+
private void assertAutowirable(Class> testClass) throws NoSuchMethodException {
Constructor> constructor = testClass.getDeclaredConstructor();
assertThat(TestConstructorUtils.isAutowirableConstructor(constructor, testClass)).isTrue();
@@ -77,14 +101,20 @@ public class TestConstructorUtilsTests {
}
private void setGlobalFlag() {
- System.setProperty(TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME, "true");
+ setGlobalFlag(ALL.name());
+ }
+
+ private void setGlobalFlag(String flag) {
+ SpringProperties.setProperty(TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME, flag);
}
static class NotAutowirableTestCase {
}
- @TestConstructor(autowire = false)
+ // The following declaration simply verifies that @Autowired on the constructor takes
+ // precedence.
+ @TestConstructor(autowireMode = ANNOTATED)
static class AutowiredAnnotationTestCase {
@Autowired
@@ -92,14 +122,14 @@ public class TestConstructorUtilsTests {
}
}
- @TestConstructor(autowire = true)
+ @TestConstructor(autowireMode = ALL)
static class TestConstructorAnnotationTestCase {
}
static class AutomaticallyAutowiredTestCase {
}
- @TestConstructor(autowire = false)
+ @TestConstructor(autowireMode = ANNOTATED)
static class TestConstructorAnnotationOverridesGlobalFlagTestCase {
}
diff --git a/src/docs/asciidoc/testing.adoc b/src/docs/asciidoc/testing.adoc
index 70826cb53b2..d7970a419e0 100644
--- a/src/docs/asciidoc/testing.adoc
+++ b/src/docs/asciidoc/testing.adoc
@@ -1287,24 +1287,24 @@ for further details.
[[integration-testing-annotations-testconstructor]]
===== `@TestConstructor`
-`@TestConstructor` is a type-level annotation that is used to configure whether a test
-class constructor should be automatically autowired from components in the test's
+`@TestConstructor` is a type-level annotation that is used to configure how the parameters
+of a test class constructor are autowired from components in the test's
`ApplicationContext`.
If `@TestConstructor` is not present or meta-present on a test class, the default _test
-constructor autowire_ mode will be used. See the tip below for details on how to change
+constructor autowire mode_ will be used. See the tip below for details on how to change
the default mode. Note, however, that a local declaration of `@Autowired` on a
constructor takes precedence over both `@TestConstructor` and the default mode.
-.Configuring the default test constructor autowire mode
+.Changing the default test constructor autowire mode
[TIP]
=====
-The default _test constructor autowire_ mode can be configured by setting the
-`spring.test.constructor.autowire` JVM system property to `true`. Alternatively, the
-default mode may be configured via the `SpringProperties` mechanism.
+The default _test constructor autowire mode_ can be changed by setting the
+`spring.test.constructor.autowire.mode` JVM system property to `all`. Alternatively, the
+default mode may be changed via the `SpringProperties` mechanism.
-If the `spring.test.constructor.autowire` property is not set, test class constructors
-will not be automatically autowired.
+If the `spring.test.constructor.autowire.mode` property is not set, test class
+constructors will not be automatically autowired.
=====
NOTE: As of Spring Framework 5.2, `@TestConstructor` is only supported in conjunction
@@ -4486,12 +4486,12 @@ the constructor is considered to be _autowirable_. A constructor is considered t
autowirable if one of the following conditions is met (in order of precedence).
* The constructor is annotated with `@Autowired`.
-* `@TestConstructor` is present or meta-present on the test class with the `autowire`
- attribute set to `true`.
-* The default _test constructor autowire_ mode is set to `true`.
+* `@TestConstructor` is present or meta-present on the test class with the `autowireMode`
+ attribute set to `ALL`.
+* The default _test constructor autowire mode_ has been changed to `ALL`.
See <> for details on the use of
-`@TestConstructor` and how to set the global _test constructor autowire_ mode.
+`@TestConstructor` and how to change the global _test constructor autowire mode_.
WARNING: If the constructor for a test class is considered to be _autowirable_, Spring
assumes the responsibility for resolving arguments for all parameters in the constructor.
@@ -4540,9 +4540,9 @@ In the following example, Spring injects the `OrderService` bean from the
Note that this feature lets test dependencies be `final` and therefore immutable.
-If the `spring.test.constructor.autowire` property is to `true` (see
+If the `spring.test.constructor.autowire.mode` property is to `all` (see
<>), we can omit the declaration of
-`@Autowired` on the constructor in the previous example resulting in the following.
+`@Autowired` on the constructor in the previous example, resulting in the following.
[source,java,indent=0]
[subs="verbatim,quotes"]