Skip to content

Commit 8f45eea

Browse files
committed
Find repeatable @⁠ExtendWith meta-annotations on fields again
JUnit Jupiter 5.11 introduced a regression regarding extension registration via fields. Specifically, repeated @⁠ExtendWith annotations on composed annotations were no longer found. This commit fixes that regression by reintroducing support for finding @⁠ExtendWith on custom composed annotations when @⁠ExtendWith is used as a repeatable annotation. Fixes #4054 (cherry picked from commit d274794)
1 parent b451122 commit 8f45eea

File tree

3 files changed

+103
-4
lines changed

3 files changed

+103
-4
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-5.11.3.adoc

+4-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ on GitHub.
3535
[[release-notes-5.11.3-junit-jupiter-bug-fixes]]
3636
==== Bug Fixes
3737

38-
* ❓
38+
* Extensions can once again be registered via multiple `@ExtendWith`
39+
meta-annotations on the same composed annotation on a field within a
40+
test class.
41+
3942

4043
[[release-notes-5.11.3-junit-jupiter-deprecations-and-breaking-changes]]
4144
==== Deprecations and Breaking Changes

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ExtensionUtils.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,7 @@ static void registerExtensionsFromExecutableParameters(ExtensionRegistrar regist
201201
* @since 5.11
202202
*/
203203
private static Stream<Field> streamExtensionRegisteringFields(Class<?> clazz, Predicate<Field> predicate) {
204-
Predicate<Field> composedPredicate = predicate.and(
205-
field -> isAnnotated(field, ExtendWith.class) || isAnnotated(field, RegisterExtension.class));
206-
return streamFields(clazz, composedPredicate, TOP_DOWN)//
204+
return streamFields(clazz, predicate.and(registersExtension), TOP_DOWN)//
207205
.sorted(orderComparator);
208206
}
209207

@@ -236,4 +234,14 @@ private static int getOrder(Field field) {
236234
return findAnnotation(field, Order.class).map(Order::value).orElse(Order.DEFAULT);
237235
}
238236

237+
/**
238+
* {@link Predicate} which determines if a {@link Field} registers an extension via
239+
* {@link RegisterExtension @RegisterExtension} or {@link ExtendWith @ExtendWith}.
240+
*
241+
* @since 5.11.3
242+
*/
243+
private static final Predicate<Field> registersExtension = //
244+
field -> isAnnotated(field, RegisterExtension.class)
245+
|| !findRepeatableAnnotations(field, ExtendWith.class).isEmpty();
246+
239247
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2015-2024 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.jupiter.engine.descriptor;
12+
13+
import static org.mockito.ArgumentMatchers.any;
14+
import static org.mockito.ArgumentMatchers.eq;
15+
import static org.mockito.Mockito.mock;
16+
import static org.mockito.Mockito.verify;
17+
18+
import java.lang.annotation.Retention;
19+
import java.lang.annotation.RetentionPolicy;
20+
import java.lang.reflect.Field;
21+
import java.util.function.Function;
22+
23+
import org.junit.jupiter.api.Test;
24+
import org.junit.jupiter.api.extension.ExtendWith;
25+
import org.junit.jupiter.api.extension.Extension;
26+
import org.junit.jupiter.api.extension.RegisterExtension;
27+
import org.junit.jupiter.engine.extension.ExtensionRegistrar;
28+
29+
/**
30+
* Tests for {@link ExtensionUtils}.
31+
*
32+
* @since 5.11.3
33+
*/
34+
class ExtensionsUtilsTests {
35+
36+
@Test
37+
void registerExtensionsViaStaticFields() throws Exception {
38+
Field field = TestCase.class.getDeclaredField("staticField");
39+
ExtensionRegistrar registrar = mock();
40+
ExtensionUtils.registerExtensionsFromStaticFields(registrar, TestCase.class);
41+
verify(registrar).registerExtension(Extension1.class);
42+
verify(registrar).registerExtension(Extension2.class);
43+
verify(registrar).registerExtension(TestCase.staticField, field);
44+
}
45+
46+
@Test
47+
@SuppressWarnings("unchecked")
48+
void registerExtensionsViaInstanceFields() throws Exception {
49+
Class<TestCase> testClass = TestCase.class;
50+
Field field = testClass.getDeclaredField("instanceField");
51+
ExtensionRegistrar registrar = mock();
52+
ExtensionUtils.registerExtensionsFromInstanceFields(registrar, testClass);
53+
verify(registrar).registerExtension(Extension1.class);
54+
verify(registrar).registerExtension(Extension2.class);
55+
verify(registrar).registerUninitializedExtension(eq(testClass), eq(field), any(Function.class));
56+
}
57+
58+
static class Extension1 implements Extension {
59+
}
60+
61+
static class Extension2 implements Extension {
62+
}
63+
64+
static class Extension3 implements Extension {
65+
}
66+
67+
static class Extension4 implements Extension {
68+
}
69+
70+
@Retention(RetentionPolicy.RUNTIME)
71+
@ExtendWith(Extension1.class)
72+
@ExtendWith(Extension2.class)
73+
@interface UseCustomExtensions {
74+
}
75+
76+
static class TestCase {
77+
78+
@UseCustomExtensions
79+
@RegisterExtension
80+
static Extension3 staticField = new Extension3();
81+
82+
@UseCustomExtensions
83+
@RegisterExtension
84+
Extension4 instanceField = new Extension4();
85+
86+
}
87+
88+
}

0 commit comments

Comments
 (0)