提交 fc38bb4f 编写于 作者: S Sam Brannen

Change @TestConstructor.autowire attribute into an enum

Prior to this commit, @TestConstructor supported a boolean `autowire`
attribute which naturally limited the configuration to two states: on
or off. Since we may need to support additional autowiring modes in the
future, the use of a boolean is limiting.

This commit address this issue by introducing a new AutowireMode enum
in @TestConstructor with ALL and ANNOTATED constants. In addition, the
attribute has been renamed to `autowireMode`, and the system property
has been renamed to `spring.test.constructor.autowire.mode` for greater
clarity of purpose.

Closes gh-23224
上级 c8f8dfa3
......@@ -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}.
*
* <p>If {@code @TestConstructor} is not <em>present</em> or <em>meta-present</em>
* on a test class, the default <em>test constructor autowire</em> 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 <em>test constructor autowire mode</em> 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;
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom
* <em>composed annotations</em>.
*
* <p>As of Spring Framework 5.2, this annotation is only supported in conjunction with
* the {@link org.springframework.test.context.junit.jupiter.SpringExtension
* <p>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 &mdash; 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 <em>test constructor autowire</em>
* mode: {@value #TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME}.
* JVM system property used to change the default <em>test constructor
* autowire mode</em>: {@value #TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME}.
* <p>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.
* <pre style="code">-Dspring.test.constructor.autowire.mode=all</pre>
* <p>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.
* <p>May alternatively be configured via the
* {@link org.springframework.core.SpringProperties SpringProperties} mechanism.
* <p>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 <em>test constructor autowire</em> mode for the
* current test class.
* Flag for setting the <em>test constructor {@linkplain AutowireMode autowire
* mode}</em> for the current test class.
* <p>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;
}
}
......@@ -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 {
* <li>The constructor is annotated with {@link Autowired @Autowired}.</li>
* <li>{@link TestConstructor @TestConstructor} is <em>present</em> or
* <em>meta-present</em> on the test class with
* {@link TestConstructor#autowire autowire} set to {@code true}.</li>
* <li>The default <em>test constructor autowire</em> mode is set to {@code true}
* (see {@link TestConstructor#TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME}).</li>
* {@link TestConstructor#autowireMode() autowireMode} set to
* {@link AutowireMode#ALL ALL}.</li>
* <li>The default <em>test constructor autowire mode</em> has been changed
* to {@code ALL} (see
* {@link TestConstructor#TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME}).</li>
* </ol>
*
* @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);
}
}
......@@ -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) {
......
......@@ -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 {
}
......
......@@ -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 <<integration-testing-annotations-testconstructor>> 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
<<integration-testing-annotations-testconstructor>>), 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"]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册