@@ -22,28 +22,31 @@ Java's <<extensions-registration-automatic,`ServiceLoader`>> mechanism.
22
22
Developers can register one or more extensions _declaratively_ by annotating a test
23
23
interface, test class, test method, or custom _<<writing-tests-meta-annotations,composed
24
24
annotation>>_ with `@ExtendWith(...)` and supplying class references for the extensions to
25
- register. As of JUnit Jupiter 5.8, `@ExtendWith` may also be declared on fields and on
25
+ register. As of JUnit Jupiter 5.8, `@ExtendWith` may also be declared on fields or on
26
26
parameters in test class constructors, in test methods, and in `@BeforeAll`, `@AfterAll`,
27
27
`@BeforeEach`, and `@AfterEach` lifecycle methods.
28
28
29
- For example, to register a custom `RandomParametersExtension` for a particular test
30
- method, you would annotate the test method as follows.
29
+ For example, to register a `WebServerExtension` for a particular test method, you would
30
+ annotate the test method as follows. We assume the `WebServerExtension` starts a local web
31
+ server and injects the server's URL into parameters annotated with `@WebServerUrl`.
31
32
32
33
[source,java,indent=0]
33
34
----
34
- @ExtendWith(RandomParametersExtension.class)
35
35
@Test
36
- void test(@Random int i) {
37
- // ...
36
+ @ExtendWith(WebServerExtension.class)
37
+ void getProductList(@WebServerUrl String serverUrl) {
38
+ WebClient webClient = new WebClient();
39
+ // Use WebClient to connect to web server using serverUrl and verify response
40
+ assertEquals(200, webClient.get(serverUrl + "/products").getResponseStatus());
38
41
}
39
42
----
40
43
41
- To register a custom `RandomParametersExtension ` for all tests in a particular class and
42
- its subclasses, you would annotate the test class as follows.
44
+ To register the `WebServerExtension ` for all tests in a particular class and its
45
+ subclasses, you would annotate the test class as follows.
43
46
44
47
[source,java,indent=0]
45
48
----
46
- @ExtendWith(RandomParametersExtension .class)
49
+ @ExtendWith(WebServerExtension .class)
47
50
class MyTests {
48
51
// ...
49
52
}
@@ -73,15 +76,16 @@ class MySecondTests {
73
76
[TIP]
74
77
.Extension Registration Order
75
78
====
76
- Extensions registered declaratively via `@ExtendWith` will be executed in the order in
77
- which they are declared in the source code. For example, the execution of tests in both
78
- `MyFirstTests` and `MySecondTests` will be extended by the `DatabaseExtension ` and
79
- `WebServerExtension`, **in exactly that order**.
79
+ Extensions registered declaratively via `@ExtendWith` at the class level, method level, or
80
+ parameter level will be executed in the order in which they are declared in the source
81
+ code. For example, the execution of tests in both `MyFirstTests ` and `MySecondTests` will
82
+ be extended by the `DatabaseExtension` and `WebServerExtension`, **in exactly that order**.
80
83
====
81
84
82
85
If you wish to combine multiple extensions in a reusable way, you can define a custom
83
86
_<<writing-tests-meta-annotations,composed annotation>>_ and use `@ExtendWith` as a
84
- _meta-annotation_:
87
+ _meta-annotation_ as in the following code listing. Then `@DatabaseAndWebServerExtension`
88
+ can be used in place of `@ExtendWith({ DatabaseExtension.class, WebServerExtension.class })`.
85
89
86
90
[source,java,indent=0]
87
91
----
@@ -92,6 +96,64 @@ public @interface DatabaseAndWebServerExtension {
92
96
}
93
97
----
94
98
99
+ The above examples demonstrate how `@ExtendWith` can be applied at the class level or at
100
+ the method level; however, for certain use cases it makes sense for an extension to be
101
+ registered declaratively at the field or parameter level. Consider a
102
+ `RandomNumberExtension` that generates random numbers that can be injected into a field or
103
+ via a parameter in a constructor, test method, or lifecycle method. If the extension
104
+ provides a `@Random` annotation that is meta-annotated with
105
+ `@ExtendWith(RandomNumberExtension.class)` (see listing below), the extension can be used
106
+ transparently as in the following `RandomNumberTests` example.
107
+
108
+ [source,java,indent=0]
109
+ ----
110
+ @Target({ ElementType.FIELD, ElementType.PARAMETER })
111
+ @Retention(RetentionPolicy.RUNTIME)
112
+ @ExtendWith(RandomNumberExtension.class)
113
+ public @interface Random {
114
+ }
115
+ ----
116
+
117
+ [source,java,indent=0]
118
+ ----
119
+ class RandomNumberTests {
120
+
121
+ // use random number field in test methods and @BeforeEach
122
+ // or @AfterEach lifecycle methods
123
+ @Random
124
+ private int randomNumber1;
125
+
126
+ RandomNumberTests(@Random int randomNumber2) {
127
+ // use random number in constructor
128
+ }
129
+
130
+ @BeforeEach
131
+ void beforeEach(@Random int randomNumber3) {
132
+ // use random number in @BeforeEach method
133
+ }
134
+
135
+ @Test
136
+ void test(@Random int randomNumber4) {
137
+ // use random number in test method
138
+ }
139
+ }
140
+ ----
141
+
142
+ [TIP]
143
+ .Extension Registration Order for `@ExtendWith` on Fields
144
+ ====
145
+ Extensions registered declaratively via `@ExtendWith` on fields will be ordered relative
146
+ to `@RegisterExtension` fields and other `@ExtendWith` fields using an algorithm that is
147
+ deterministic but intentionally nonobvious. However, `@ExtendWith` fields can be ordered
148
+ using the `@Order` annotation. See the <<extensions-registration-programmatic-order,
149
+ Extension Registration Order>> tip for `@RegisterExtension` fields for details.
150
+ ====
151
+
152
+ NOTE: `@ExtendWith` fields may be either `static` or non-static. The documentation on
153
+ <<extensions-registration-programmatic-static-fields, Static Fields>> and
154
+ <<extensions-registration-programmatic-instance-fields, Instance Fields>> for
155
+ `@RegisterExtension` fields also applies to `@ExtendWith` fields.
156
+
95
157
[[extensions-registration-programmatic]]
96
158
==== Programmatic Extension Registration
97
159
@@ -108,23 +170,23 @@ extension's constructor, a static factory method, or a builder API.
108
170
[TIP]
109
171
.Extension Registration Order
110
172
====
111
- By default, extensions registered programmatically via `@RegisterExtension` will be
112
- ordered using an algorithm that is deterministic but intentionally nonobvious. This
113
- ensures that subsequent runs of a test suite execute extensions in the same order, thereby
114
- allowing for repeatable builds. However, there are times when extensions need to be
115
- registered in an explicit order. To achieve that, annotate `@RegisterExtension` fields
116
- with `{Order}`.
117
-
118
- Any `@RegisterExtension` field not annotated with `@Order` will be ordered using the
119
- _default_ order which has a value of `Integer.MAX_VALUE / 2`. This allows `@Order`
120
- annotated extension fields to be explicitly ordered before or after non-annotated
121
- extension fields. Extensions with an explicit order value less than the default order
122
- value will be registered before non-annotated extensions. Similarly, extensions with an
123
- explicit order value greater than the default order value will be registered after
124
- non-annotated extensions. For example, assigning an extension an explicit order value that
125
- is greater than the default order value allows _before_ callback extensions to be
126
- registered last and _after_ callback extensions to be registered first, relative to other
127
- programmatically registered extensions.
173
+ By default, extensions registered programmatically via `@RegisterExtension` or
174
+ declaratively via `@ExtendWith` on fields will be ordered using an algorithm that is
175
+ deterministic but intentionally nonobvious. This ensures that subsequent runs of a test
176
+ suite execute extensions in the same order, thereby allowing for repeatable builds.
177
+ However, there are times when extensions need to be registered in an explicit order. To
178
+ achieve that, annotate `@RegisterExtension` fields or `@ExtendWith` fields with `{Order}`.
179
+
180
+ Any `@RegisterExtension` field or `@ExtendWith` field not annotated with `@Order` will be
181
+ ordered using the _default_ order which has a value of `Integer.MAX_VALUE / 2`. This
182
+ allows `@Order` annotated extension fields to be explicitly ordered before or after
183
+ non-annotated extension fields. Extensions with an explicit order value less than the
184
+ default order value will be registered before non-annotated extensions. Similarly,
185
+ extensions with an explicit order value greater than the default order value will be
186
+ registered after non-annotated extensions. For example, assigning an extension an explicit
187
+ order value that is greater than the default order value allows _before_ callback
188
+ extensions to be registered last and _after_ callback extensions to be registered first,
189
+ relative to other programmatically registered extensions.
128
190
====
129
191
130
192
NOTE: `@RegisterExtension` fields must not be `null` (at evaluation time) but may be
@@ -151,19 +213,18 @@ lifecycle methods annotated with `@BeforeAll` or `@AfterAll` as well as `@Before
151
213
`server` field if necessary.
152
214
153
215
[source,java,indent=0]
154
- .An extension registered via a static field
216
+ .Registering an extension via a static field in Java
155
217
----
156
218
include::{testDir}/example/registration/WebServerDemo.java[tags=user_guide]
157
219
----
158
220
159
221
[[extensions-registration-programmatic-static-fields-kotlin]]
160
- ===== Static Fields in Kotlin
222
+ ====== Static Fields in Kotlin
161
223
162
224
The Kotlin programming language does not have the concept of a `static` field. However,
163
- the compiler can be instructed to generate static fields using annotations. Since, as
164
- stated earlier, `@RegisterExtension` fields must not be `null`, one **cannot** use the
165
- `@JvmStatic` annotation in Kotlin as it generates `private` fields. Rather, the
166
- `@JvmField` annotation must be used.
225
+ the compiler can be instructed to generate a `private static` field using the `@JvmStatic`
226
+ annotation in Kotlin. If you want the Kotlin compiler to generate a `public static` field,
227
+ you can use the `@JvmField` annotation instead.
167
228
168
229
The following example is a version of the `WebServerDemo` from the previous section that
169
230
has been ported to Kotlin.
0 commit comments