@@ -22,28 +22,31 @@ Java's <<extensions-registration-automatic,`ServiceLoader`>> mechanism.
2222Developers can register one or more extensions _declaratively_ by annotating a test
2323interface, test class, test method, or custom _<<writing-tests-meta-annotations,composed
2424annotation>>_ 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
2626parameters in test class constructors, in test methods, and in `@BeforeAll`, `@AfterAll`,
2727`@BeforeEach`, and `@AfterEach` lifecycle methods.
2828
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`.
3132
3233[source,java,indent=0]
3334----
34- @ExtendWith(RandomParametersExtension.class)
3535@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());
3841}
3942----
4043
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.
4346
4447[source,java,indent=0]
4548----
46- @ExtendWith(RandomParametersExtension .class)
49+ @ExtendWith(WebServerExtension .class)
4750class MyTests {
4851 // ...
4952}
@@ -73,15 +76,16 @@ class MySecondTests {
7376[TIP]
7477.Extension Registration Order
7578====
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**.
8083====
8184
8285If you wish to combine multiple extensions in a reusable way, you can define a custom
8386_<<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 })`.
8589
8690[source,java,indent=0]
8791----
@@ -92,6 +96,64 @@ public @interface DatabaseAndWebServerExtension {
9296}
9397----
9498
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+
95157[[extensions-registration-programmatic]]
96158==== Programmatic Extension Registration
97159
@@ -108,23 +170,23 @@ extension's constructor, a static factory method, or a builder API.
108170[TIP]
109171.Extension Registration Order
110172====
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.
128190====
129191
130192NOTE: `@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
151213`server` field if necessary.
152214
153215[source,java,indent=0]
154- .An extension registered via a static field
216+ .Registering an extension via a static field in Java
155217----
156218include::{testDir}/example/registration/WebServerDemo.java[tags=user_guide]
157219----
158220
159221[[extensions-registration-programmatic-static-fields-kotlin]]
160- ===== Static Fields in Kotlin
222+ ====== Static Fields in Kotlin
161223
162224The 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.
167228
168229The following example is a version of the `WebServerDemo` from the previous section that
169230has been ported to Kotlin.
0 commit comments