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
This commit is contained in:
parent
c8f8dfa39e
commit
fc38bb4fc6
|
|
@ -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 — 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"]
|
||||
|
|
|
|||
Loading…
Reference in New Issue