Skip to content

Commit fc38bb4

Browse files
committed
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
1 parent c8f8dfa commit fc38bb4

File tree

5 files changed

+145
-51
lines changed

5 files changed

+145
-51
lines changed

spring-test/src/main/java/org/springframework/test/context/TestConstructor.java

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,23 @@
2525

2626
/**
2727
* {@code @TestConstructor} is a type-level annotation that is used to configure
28-
* whether a test class constructor should be automatically autowired from
29-
* components in the test's {@link org.springframework.context.ApplicationContext
28+
* how the parameters of a test class constructor are autowired from components
29+
* in the test's {@link org.springframework.context.ApplicationContext
3030
* ApplicationContext}.
3131
*
3232
* <p>If {@code @TestConstructor} is not <em>present</em> or <em>meta-present</em>
33-
* on a test class, the default <em>test constructor autowire</em> mode will be used.
34-
* See {@link #TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME} for details on how to change
35-
* the default mode. Note, however, that a local declaration of
33+
* on a test class, the default <em>test constructor autowire mode</em> will be
34+
* used. See {@link #TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME} for details on
35+
* how to change the default mode. Note, however, that a local declaration of
3636
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired} on
3737
* a constructor takes precedence over both {@code @TestConstructor} and the default
3838
* mode.
3939
*
4040
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom
4141
* <em>composed annotations</em>.
4242
*
43-
* <p>As of Spring Framework 5.2, this annotation is only supported in conjunction with
44-
* the {@link org.springframework.test.context.junit.jupiter.SpringExtension
43+
* <p>As of Spring Framework 5.2, this annotation is only supported in conjunction
44+
* with the {@link org.springframework.test.context.junit.jupiter.SpringExtension
4545
* SpringExtension} for use with JUnit Jupiter. Note that the {@code SpringExtension} is
4646
* often automatically registered for you &mdash; for example, when using annotations such as
4747
* {@link org.springframework.test.context.junit.jupiter.SpringJUnitConfig @SpringJUnitConfig} and
@@ -66,29 +66,64 @@
6666
public @interface TestConstructor {
6767

6868
/**
69-
* System property used to configure the default <em>test constructor autowire</em>
70-
* mode: {@value #TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME}.
69+
* JVM system property used to change the default <em>test constructor
70+
* autowire mode</em>: {@value #TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME}.
71+
* <p>Acceptable values include enum constants defined in {@link AutowireMode},
72+
* ignoring case. For example, the default may be changed to {@link AutowireMode#ALL}
73+
* by supplying the following JVM system property via the command line.
74+
* <pre style="code">-Dspring.test.constructor.autowire.mode=all</pre>
75+
* <p>If the property is not set to {@code ALL}, parameters for test class
76+
* constructors will be autowired according to {@link AutowireMode#ANNOTATED}
77+
* semantics by default.
7178
* <p>May alternatively be configured via the
72-
* {@link org.springframework.core.SpringProperties SpringProperties} mechanism.
73-
* <p>If the property is not set, test class constructors will not be automatically
74-
* autowired.
75-
* @see #autowire
79+
* {@link org.springframework.core.SpringProperties SpringProperties}
80+
* mechanism.
81+
* @see #autowireMode
7682
*/
77-
String TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME = "spring.test.constructor.autowire";
83+
String TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME = "spring.test.constructor.autowire.mode";
7884

7985

8086
/**
81-
* Flag for setting the <em>test constructor autowire</em> mode for the
82-
* current test class.
87+
* Flag for setting the <em>test constructor {@linkplain AutowireMode autowire
88+
* mode}</em> for the current test class.
8389
* <p>Setting this flag overrides the global default. See
84-
* {@link #TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME} for details on how to
85-
* change the global default.
86-
* @return {@code true} if all test constructor arguments should be autowired
87-
* from the test's {@link org.springframework.context.ApplicationContext
88-
* ApplicationContext}
89-
* @see #TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME
90+
* {@link #TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME} for details on how
91+
* to change the global default.
92+
* @return an {@link AutowireMode} to take precedence over the global default
93+
* @see #TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME
9094
* @see org.springframework.beans.factory.annotation.Autowired @Autowired
95+
* @see AutowireMode#ALL
96+
* @see AutowireMode#ANNOTATED
9197
*/
92-
boolean autowire();
98+
AutowireMode autowireMode();
99+
100+
101+
/**
102+
* Defines autowiring modes for parameters in a test constructor.
103+
* @see #ALL
104+
* @see #ANNOTATED
105+
*/
106+
enum AutowireMode {
107+
108+
/**
109+
* All test constructor parameters will be autowired as if the constructor
110+
* itself were annotated with
111+
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired}.
112+
* @see #ANNOTATED
113+
*/
114+
ALL,
115+
116+
/**
117+
* Each individual test constructor parameter will only be autowired if it
118+
* is annotated with
119+
* {@link org.springframework.beans.factory.annotation.Autowired @Autowired},
120+
* {@link org.springframework.beans.factory.annotation.Qualifier @Qualifier},
121+
* or {@link org.springframework.beans.factory.annotation.Value @Value},
122+
* or if the constructor itself is annotated with {@code @Autowired}.
123+
* @see #ALL
124+
*/
125+
ANNOTATED;
126+
127+
}
93128

94129
}

spring-test/src/main/java/org/springframework/test/context/support/TestConstructorUtils.java

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@
1919
import java.lang.reflect.Constructor;
2020
import java.lang.reflect.Executable;
2121

22+
import org.apache.commons.logging.Log;
23+
import org.apache.commons.logging.LogFactory;
24+
2225
import org.springframework.beans.factory.annotation.Autowired;
2326
import org.springframework.core.SpringProperties;
2427
import org.springframework.core.annotation.AnnotatedElementUtils;
2528
import org.springframework.test.context.TestConstructor;
29+
import org.springframework.test.context.TestConstructor.AutowireMode;
2630

2731
/**
2832
* Utility methods for working with {@link TestConstructor @TestConstructor}.
@@ -35,10 +39,12 @@
3539
*/
3640
public abstract class TestConstructorUtils {
3741

42+
private static final Log logger = LogFactory.getLog(TestConstructorUtils.class);
43+
44+
3845
private TestConstructorUtils() {
3946
}
4047

41-
4248
/**
4349
* Determine if the supplied executable for the given test class is an
4450
* autowirable constructor.
@@ -67,9 +73,11 @@ public static boolean isAutowirableConstructor(Executable executable, Class<?> t
6773
* <li>The constructor is annotated with {@link Autowired @Autowired}.</li>
6874
* <li>{@link TestConstructor @TestConstructor} is <em>present</em> or
6975
* <em>meta-present</em> on the test class with
70-
* {@link TestConstructor#autowire autowire} set to {@code true}.</li>
71-
* <li>The default <em>test constructor autowire</em> mode is set to {@code true}
72-
* (see {@link TestConstructor#TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME}).</li>
76+
* {@link TestConstructor#autowireMode() autowireMode} set to
77+
* {@link AutowireMode#ALL ALL}.</li>
78+
* <li>The default <em>test constructor autowire mode</em> has been changed
79+
* to {@code ALL} (see
80+
* {@link TestConstructor#TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME}).</li>
7381
* </ol>
7482
*
7583
* @param constructor a constructor for the test class
@@ -82,13 +90,32 @@ public static boolean isAutowirableConstructor(Constructor<?> constructor, Class
8290
if (AnnotatedElementUtils.hasAnnotation(constructor, Autowired.class)) {
8391
return true;
8492
}
93+
94+
// Default to Autowire.ANNOTATED semantics.
95+
AutowireMode autowireMode = AutowireMode.ANNOTATED;
96+
8597
// Is the test class annotated with @TestConstructor?
8698
TestConstructor testConstructor = AnnotatedElementUtils.findMergedAnnotation(testClass, TestConstructor.class);
8799
if (testConstructor != null) {
88-
return testConstructor.autowire();
100+
autowireMode = testConstructor.autowireMode();
89101
}
90-
// Else use global default.
91-
return SpringProperties.getFlag(TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME);
102+
else {
103+
// Custom global default?
104+
String value = SpringProperties.getProperty(TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME);
105+
if (value != null) {
106+
try {
107+
autowireMode = AutowireMode.valueOf(value.trim().toUpperCase());
108+
}
109+
catch (Exception ex) {
110+
if (logger.isDebugEnabled()) {
111+
logger.debug(String.format("Failed to parse autowire mode '%s' for property '%s': %s", value,
112+
TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME, ex.getMessage()));
113+
}
114+
}
115+
}
116+
}
117+
118+
return (autowireMode == AutowireMode.ALL);
92119
}
93120

94121
}

spring-test/src/test/java/org/springframework/test/context/junit/jupiter/TestConstructorAnnotationIntegrationTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.test.context.junit.jupiter.comics.Person;
2929

3030
import static org.assertj.core.api.Assertions.assertThat;
31+
import static org.springframework.test.context.TestConstructor.AutowireMode.ALL;
3132

3233
/**
3334
* Integration tests which demonstrate support for automatically
@@ -45,14 +46,15 @@
4546
*/
4647
@SpringJUnitConfig(TestConfig.class)
4748
@TestPropertySource(properties = "enigma = 42")
48-
@TestConstructor(autowire = true)
49+
@TestConstructor(autowireMode = ALL)
4950
class TestConstructorAnnotationIntegrationTests {
5051

5152
final ApplicationContext applicationContext;
5253
final Person dilbert;
5354
final Dog dog;
5455
final Integer enigma;
5556

57+
5658
TestConstructorAnnotationIntegrationTests(ApplicationContext applicationContext, Person dilbert, Dog dog,
5759
@Value("${enigma}") Integer enigma) {
5860

spring-test/src/test/java/org/springframework/test/context/support/TestConstructorUtilsTests.java

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@
2222
import org.junit.Test;
2323

2424
import org.springframework.beans.factory.annotation.Autowired;
25+
import org.springframework.core.SpringProperties;
2526
import org.springframework.test.context.TestConstructor;
2627

2728
import static org.assertj.core.api.Assertions.assertThat;
29+
import static org.springframework.test.context.TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME;
30+
import static org.springframework.test.context.TestConstructor.AutowireMode.ALL;
31+
import static org.springframework.test.context.TestConstructor.AutowireMode.ANNOTATED;
2832

2933
/**
3034
* Unit tests for {@link TestConstructorUtils}.
@@ -36,7 +40,7 @@ public class TestConstructorUtilsTests {
3640

3741
@After
3842
public void clearGlobalFlag() {
39-
System.clearProperty(TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME);
43+
setGlobalFlag(null);
4044
}
4145

4246
@Test
@@ -66,6 +70,26 @@ public void automaticallyAutowiredButOverriddenLocally() throws Exception {
6670
assertNotAutowirable(TestConstructorAnnotationOverridesGlobalFlagTestCase.class);
6771
}
6872

73+
@Test
74+
public void globalFlagVariations() throws Exception {
75+
Class<?> testClass = AutomaticallyAutowiredTestCase.class;
76+
77+
setGlobalFlag(ALL.name());
78+
assertAutowirable(testClass);
79+
80+
setGlobalFlag(ALL.name().toLowerCase());
81+
assertAutowirable(testClass);
82+
83+
setGlobalFlag("\t" + ALL.name().toLowerCase() + " ");
84+
assertAutowirable(testClass);
85+
86+
setGlobalFlag("bogus");
87+
assertNotAutowirable(testClass);
88+
89+
setGlobalFlag(" ");
90+
assertNotAutowirable(testClass);
91+
}
92+
6993
private void assertAutowirable(Class<?> testClass) throws NoSuchMethodException {
7094
Constructor<?> constructor = testClass.getDeclaredConstructor();
7195
assertThat(TestConstructorUtils.isAutowirableConstructor(constructor, testClass)).isTrue();
@@ -77,29 +101,35 @@ private void assertNotAutowirable(Class<?> testClass) throws NoSuchMethodExcepti
77101
}
78102

79103
private void setGlobalFlag() {
80-
System.setProperty(TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_PROPERTY_NAME, "true");
104+
setGlobalFlag(ALL.name());
105+
}
106+
107+
private void setGlobalFlag(String flag) {
108+
SpringProperties.setProperty(TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME, flag);
81109
}
82110

83111

84112
static class NotAutowirableTestCase {
85113
}
86114

87-
@TestConstructor(autowire = false)
115+
// The following declaration simply verifies that @Autowired on the constructor takes
116+
// precedence.
117+
@TestConstructor(autowireMode = ANNOTATED)
88118
static class AutowiredAnnotationTestCase {
89119

90120
@Autowired
91121
AutowiredAnnotationTestCase() {
92122
}
93123
}
94124

95-
@TestConstructor(autowire = true)
125+
@TestConstructor(autowireMode = ALL)
96126
static class TestConstructorAnnotationTestCase {
97127
}
98128

99129
static class AutomaticallyAutowiredTestCase {
100130
}
101131

102-
@TestConstructor(autowire = false)
132+
@TestConstructor(autowireMode = ANNOTATED)
103133
static class TestConstructorAnnotationOverridesGlobalFlagTestCase {
104134
}
105135

src/docs/asciidoc/testing.adoc

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,24 +1287,24 @@ for further details.
12871287
[[integration-testing-annotations-testconstructor]]
12881288
===== `@TestConstructor`
12891289

1290-
`@TestConstructor` is a type-level annotation that is used to configure whether a test
1291-
class constructor should be automatically autowired from components in the test's
1290+
`@TestConstructor` is a type-level annotation that is used to configure how the parameters
1291+
of a test class constructor are autowired from components in the test's
12921292
`ApplicationContext`.
12931293

12941294
If `@TestConstructor` is not present or meta-present on a test class, the default _test
1295-
constructor autowire_ mode will be used. See the tip below for details on how to change
1295+
constructor autowire mode_ will be used. See the tip below for details on how to change
12961296
the default mode. Note, however, that a local declaration of `@Autowired` on a
12971297
constructor takes precedence over both `@TestConstructor` and the default mode.
12981298

1299-
.Configuring the default test constructor autowire mode
1299+
.Changing the default test constructor autowire mode
13001300
[TIP]
13011301
=====
1302-
The default _test constructor autowire_ mode can be configured by setting the
1303-
`spring.test.constructor.autowire` JVM system property to `true`. Alternatively, the
1304-
default mode may be configured via the `SpringProperties` mechanism.
1302+
The default _test constructor autowire mode_ can be changed by setting the
1303+
`spring.test.constructor.autowire.mode` JVM system property to `all`. Alternatively, the
1304+
default mode may be changed via the `SpringProperties` mechanism.
13051305
1306-
If the `spring.test.constructor.autowire` property is not set, test class constructors
1307-
will not be automatically autowired.
1306+
If the `spring.test.constructor.autowire.mode` property is not set, test class
1307+
constructors will not be automatically autowired.
13081308
=====
13091309

13101310
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
44864486
autowirable if one of the following conditions is met (in order of precedence).
44874487

44884488
* The constructor is annotated with `@Autowired`.
4489-
* `@TestConstructor` is present or meta-present on the test class with the `autowire`
4490-
attribute set to `true`.
4491-
* The default _test constructor autowire_ mode is set to `true`.
4489+
* `@TestConstructor` is present or meta-present on the test class with the `autowireMode`
4490+
attribute set to `ALL`.
4491+
* The default _test constructor autowire mode_ has been changed to `ALL`.
44924492

44934493
See <<integration-testing-annotations-testconstructor>> for details on the use of
4494-
`@TestConstructor` and how to set the global _test constructor autowire_ mode.
4494+
`@TestConstructor` and how to change the global _test constructor autowire mode_.
44954495

44964496
WARNING: If the constructor for a test class is considered to be _autowirable_, Spring
44974497
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
45404540

45414541
Note that this feature lets test dependencies be `final` and therefore immutable.
45424542

4543-
If the `spring.test.constructor.autowire` property is to `true` (see
4543+
If the `spring.test.constructor.autowire.mode` property is to `all` (see
45444544
<<integration-testing-annotations-testconstructor>>), we can omit the declaration of
4545-
`@Autowired` on the constructor in the previous example resulting in the following.
4545+
`@Autowired` on the constructor in the previous example, resulting in the following.
45464546

45474547
[source,java,indent=0]
45484548
[subs="verbatim,quotes"]

0 commit comments

Comments
 (0)