@@ -21,27 +21,32 @@ Java's <<extensions-registration-automatic,`ServiceLoader`>> mechanism.
2121
2222Developers can register one or more extensions _declaratively_ by annotating a test
2323interface, test class, test method, or custom _<<writing-tests-meta-annotations,composed
24- annotation>>_ with `@ExtendWith(...)` and supplying class references for the extensions
25- to register.
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 or on
26+ parameters in test class constructors, in test methods, and in `@BeforeAll`, `@AfterAll`,
27+ `@BeforeEach`, and `@AfterEach` lifecycle methods.
2628
27- For example, to register a custom `RandomParametersExtension` for a particular test
28- 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`.
2932
3033[source,java,indent=0]
3134----
32- @ExtendWith(RandomParametersExtension.class)
3335@Test
34- void test(@Random int i) {
35- // ...
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());
3641}
3742----
3843
39- To register a custom `RandomParametersExtension ` for all tests in a particular class and
40- 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.
4146
4247[source,java,indent=0]
4348----
44- @ExtendWith(RandomParametersExtension .class)
49+ @ExtendWith(WebServerExtension .class)
4550class MyTests {
4651 // ...
4752}
@@ -71,15 +76,16 @@ class MySecondTests {
7176[TIP]
7277.Extension Registration Order
7378====
74- Extensions registered declaratively via `@ExtendWith` will be executed in the order in
75- which they are declared in the source code. For example, the execution of tests in both
76- `MyFirstTests` and `MySecondTests` will be extended by the `DatabaseExtension ` and
77- `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**.
7883====
7984
8085If you wish to combine multiple extensions in a reusable way, you can define a custom
8186_<<writing-tests-meta-annotations,composed annotation>>_ and use `@ExtendWith` as a
82- _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 })`.
8389
8490[source,java,indent=0]
8591----
@@ -90,6 +96,64 @@ public @interface DatabaseAndWebServerExtension {
9096}
9197----
9298
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+
93157[[extensions-registration-programmatic]]
94158==== Programmatic Extension Registration
95159
@@ -106,27 +170,27 @@ extension's constructor, a static factory method, or a builder API.
106170[TIP]
107171.Extension Registration Order
108172====
109- By default, extensions registered programmatically via `@RegisterExtension` will be
110- ordered using an algorithm that is deterministic but intentionally nonobvious. This
111- ensures that subsequent runs of a test suite execute extensions in the same order, thereby
112- allowing for repeatable builds. However, there are times when extensions need to be
113- registered in an explicit order. To achieve that, annotate `@RegisterExtension` fields
114- with `{Order}`.
115-
116- Any `@RegisterExtension` field not annotated with `@Order` will be ordered using the
117- _default_ order which has a value of `Integer.MAX_VALUE / 2`. This allows `@Order`
118- annotated extension fields to be explicitly ordered before or after non-annotated
119- extension fields. Extensions with an explicit order value less than the default order
120- value will be registered before non-annotated extensions. Similarly, extensions with an
121- explicit order value greater than the default order value will be registered after
122- non-annotated extensions. For example, assigning an extension an explicit order value that
123- is greater than the default order value allows _before_ callback extensions to be
124- registered last and _after_ callback extensions to be registered first, relative to other
125- 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.
126190====
127191
128- NOTE: `@RegisterExtension` fields must not be `private` or ` null` (at evaluation time) but
129- may be either `static` or non-static.
192+ NOTE: `@RegisterExtension` fields must not be `null` (at evaluation time) but may be
193+ either `static` or non-static.
130194
131195[[extensions-registration-programmatic-static-fields]]
132196===== Static Fields
@@ -149,19 +213,18 @@ lifecycle methods annotated with `@BeforeAll` or `@AfterAll` as well as `@Before
149213`server` field if necessary.
150214
151215[source,java,indent=0]
152- .An extension registered via a static field
216+ .Registering an extension via a static field in Java
153217----
154218include::{testDir}/example/registration/WebServerDemo.java[tags=user_guide]
155219----
156220
157221[[extensions-registration-programmatic-static-fields-kotlin]]
158- ===== Static Fields in Kotlin
222+ ====== Static Fields in Kotlin
159223
160224The Kotlin programming language does not have the concept of a `static` field. However,
161- the compiler can be instructed to generate static fields using annotations. Since, as
162- stated earlier, `@RegisterExtension` fields must not be `private` nor `null`, one
163- **cannot** use the `@JvmStatic` annotation in Kotlin as it generates `private` fields.
164- Rather, the `@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.
165228
166229The following example is a version of the `WebServerDemo` from the previous section that
167230has been ported to Kotlin.
0 commit comments