Skip to content

Commit 74521ab

Browse files
committed
Introduce ExtensionContext.getEnclosingTestClasses()
Resolves #4375.
1 parent 1e135b9 commit 74521ab

File tree

10 files changed

+149
-23
lines changed

10 files changed

+149
-23
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ on GitHub.
4545
[[release-notes-5.12.1-junit-jupiter-new-features-and-improvements]]
4646
==== New Features and Improvements
4747

48-
* ❓
48+
* New `ExtensionContext.getEnclosingTestClasses()` method to help with migration away from
49+
`AnnotationSupport.findAnnotation(Class, Class, SearchOption)` (deprecated since 1.12.0)
50+
to `AnnotationSupport.findAnnotation(Class, Class, List)`.
4951

5052

5153
[[release-notes-5.12.1-junit-vintage]]

junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContext.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
* <p>{@link Extension Extensions} are provided an instance of
4141
* {@code ExtensionContext} to perform their work.
4242
*
43+
* <p>This interface is not intended to be implemented by clients.
44+
*
4345
* @since 5.0
4446
* @see Store
4547
* @see Namespace
@@ -129,6 +131,31 @@ public interface ExtensionContext {
129131
*/
130132
Optional<Class<?>> getTestClass();
131133

134+
/**
135+
* Get the enclosing test classes of the current test or container.
136+
*
137+
* <p>This method is useful to look up annotations on nested test classes
138+
* and their enclosing <em>runtime</em> types:
139+
*
140+
* <pre>{@code
141+
* AnnotationSupport.findAnnotation(
142+
* extensionContext.getRequiredTestClass(),
143+
* MyAnnotation.class,
144+
* extensionContext.getEnclosingTestClasses()
145+
* );
146+
* }</pre>
147+
*
148+
* @return an empty list if there is no class associated with the current
149+
* test or container or when it is not nested; otherwise, a list containing
150+
* the enclosing test classes in order from outermost to innermost; never
151+
* {@code null}
152+
*
153+
* @since 5.12.1
154+
* @see org.junit.platform.commons.support.AnnotationSupport#findAnnotation(Class, Class, List)
155+
*/
156+
@API(status = EXPERIMENTAL, since = "5.12.1")
157+
List<Class<?>> getEnclosingTestClasses();
158+
132159
/**
133160
* Get the <em>required</em> {@link Class} associated with the current test
134161
* or container.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import java.lang.reflect.AnnotatedElement;
1414
import java.lang.reflect.Method;
15+
import java.util.List;
1516
import java.util.Optional;
1617

1718
import org.junit.jupiter.api.TestInstance.Lifecycle;
@@ -68,6 +69,11 @@ public Optional<Class<?>> getTestClass() {
6869
return Optional.of(getTestDescriptor().getTestClass());
6970
}
7071

72+
@Override
73+
public List<Class<?>> getEnclosingTestClasses() {
74+
return getTestDescriptor().getEnclosingTestClasses();
75+
}
76+
7177
@Override
7278
public Optional<Lifecycle> getTestInstanceLifecycle() {
7379
return Optional.of(this.lifecycle);

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010

1111
package org.junit.jupiter.engine.descriptor;
1212

13+
import static java.util.Collections.emptyList;
14+
1315
import java.lang.reflect.AnnotatedElement;
1416
import java.lang.reflect.Method;
17+
import java.util.List;
1518
import java.util.Optional;
1619

1720
import org.junit.jupiter.api.TestInstance;
@@ -40,6 +43,11 @@ public Optional<Class<?>> getTestClass() {
4043
return Optional.empty();
4144
}
4245

46+
@Override
47+
public List<Class<?>> getEnclosingTestClasses() {
48+
return emptyList();
49+
}
50+
4351
@Override
4452
public Optional<TestInstance.Lifecycle> getTestInstanceLifecycle() {
4553
return Optional.empty();

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010

1111
package org.junit.jupiter.engine.descriptor;
1212

13+
import static java.util.Collections.emptyList;
14+
1315
import java.lang.reflect.AnnotatedElement;
1416
import java.lang.reflect.Method;
17+
import java.util.List;
1518
import java.util.Optional;
1619

1720
import org.junit.jupiter.api.TestInstance.Lifecycle;
@@ -43,6 +46,11 @@ public Optional<Class<?>> getTestClass() {
4346
return Optional.empty();
4447
}
4548

49+
@Override
50+
public List<Class<?>> getEnclosingTestClasses() {
51+
return emptyList();
52+
}
53+
4654
@Override
4755
public Optional<Lifecycle> getTestInstanceLifecycle() {
4856
return Optional.empty();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> getResou
106106
getTestMethod()));
107107
}
108108

109-
private List<Class<?>> getEnclosingTestClasses() {
109+
List<Class<?>> getEnclosingTestClasses() {
110110
return getParent() //
111111
.filter(ClassBasedTestDescriptor.class::isInstance) //
112112
.map(ClassBasedTestDescriptor.class::cast) //

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import java.lang.reflect.AnnotatedElement;
1414
import java.lang.reflect.Method;
15+
import java.util.List;
1516
import java.util.Optional;
1617

1718
import org.junit.jupiter.api.TestInstance.Lifecycle;
@@ -51,6 +52,11 @@ public Optional<Class<?>> getTestClass() {
5152
return Optional.of(getTestDescriptor().getTestClass());
5253
}
5354

55+
@Override
56+
public List<Class<?>> getEnclosingTestClasses() {
57+
return getTestDescriptor().getEnclosingTestClasses();
58+
}
59+
5460
@Override
5561
public Optional<Lifecycle> getTestInstanceLifecycle() {
5662
return getParent().flatMap(ExtensionContext::getTestInstanceLifecycle);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import java.lang.reflect.AnnotatedElement;
1414
import java.lang.reflect.Method;
15+
import java.util.List;
1516
import java.util.Optional;
1617

1718
import org.junit.jupiter.api.TestInstance.Lifecycle;
@@ -47,6 +48,11 @@ public Optional<Class<?>> getTestClass() {
4748
return Optional.of(getTestDescriptor().getTestClass());
4849
}
4950

51+
@Override
52+
public List<Class<?>> getEnclosingTestClasses() {
53+
return getTestDescriptor().getEnclosingTestClasses();
54+
}
55+
5056
@Override
5157
public Optional<Lifecycle> getTestInstanceLifecycle() {
5258
return getParent().flatMap(ExtensionContext::getTestInstanceLifecycle);

jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/ExtensionContextTests.java

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.junit.jupiter.api.BeforeEach;
4040
import org.junit.jupiter.api.DisplayNameGenerator;
4141
import org.junit.jupiter.api.Named;
42+
import org.junit.jupiter.api.Nested;
4243
import org.junit.jupiter.api.Tag;
4344
import org.junit.jupiter.api.Test;
4445
import org.junit.jupiter.api.extension.Extension;
@@ -111,33 +112,63 @@ void fromJupiterEngineDescriptor() {
111112
}
112113

113114
@Test
114-
@SuppressWarnings("resource")
115115
void fromClassTestDescriptor() {
116116
var nestedClassDescriptor = nestedClassDescriptor();
117117
var outerClassDescriptor = outerClassDescriptor(nestedClassDescriptor);
118+
var doublyNestedClassDescriptor = doublyNestedClassDescriptor();
119+
var methodTestDescriptor = nestedMethodDescriptor();
120+
nestedClassDescriptor.addChild(doublyNestedClassDescriptor);
121+
nestedClassDescriptor.addChild(methodTestDescriptor);
118122

119123
var outerExtensionContext = new ClassExtensionContext(null, null, outerClassDescriptor, configuration,
120124
extensionRegistry, null);
121125

122126
// @formatter:off
123127
assertAll("outerContext",
124-
() -> assertThat(outerExtensionContext.getElement()).contains(OuterClass.class),
125-
() -> assertThat(outerExtensionContext.getTestClass()).contains(OuterClass.class),
128+
() -> assertThat(outerExtensionContext.getElement()).contains(OuterClassTestCase.class),
129+
() -> assertThat(outerExtensionContext.getTestClass()).contains(OuterClassTestCase.class),
126130
() -> assertThat(outerExtensionContext.getTestInstance()).isEmpty(),
127131
() -> assertThat(outerExtensionContext.getTestMethod()).isEmpty(),
128-
() -> assertThat(outerExtensionContext.getRequiredTestClass()).isEqualTo(OuterClass.class),
132+
() -> assertThat(outerExtensionContext.getRequiredTestClass()).isEqualTo(OuterClassTestCase.class),
129133
() -> assertThrows(PreconditionViolationException.class, outerExtensionContext::getRequiredTestInstance),
130134
() -> assertThrows(PreconditionViolationException.class, outerExtensionContext::getRequiredTestMethod),
131135
() -> assertThat(outerExtensionContext.getDisplayName()).isEqualTo(outerClassDescriptor.getDisplayName()),
132136
() -> assertThat(outerExtensionContext.getParent()).isEmpty(),
133137
() -> assertThat(outerExtensionContext.getExecutionMode()).isEqualTo(ExecutionMode.SAME_THREAD),
134-
() -> assertThat(outerExtensionContext.getExtensions(PreInterruptCallback.class)).isEmpty()
138+
() -> assertThat(outerExtensionContext.getExtensions(PreInterruptCallback.class)).isEmpty(),
139+
() -> assertThat(outerExtensionContext.getEnclosingTestClasses()).isEmpty()
135140
);
136141
// @formatter:on
137142

138143
var nestedExtensionContext = new ClassExtensionContext(outerExtensionContext, null, nestedClassDescriptor,
139144
configuration, extensionRegistry, null);
140-
assertThat(nestedExtensionContext.getParent()).containsSame(outerExtensionContext);
145+
// @formatter:off
146+
assertAll("nestedContext",
147+
() -> assertThat(nestedExtensionContext.getParent()).containsSame(outerExtensionContext),
148+
() -> assertThat(nestedExtensionContext.getTestClass()).contains(OuterClassTestCase.NestedClass.class),
149+
() -> assertThat(nestedExtensionContext.getEnclosingTestClasses()).containsExactly(OuterClassTestCase.class)
150+
);
151+
// @formatter:on
152+
153+
var doublyNestedExtensionContext = new ClassExtensionContext(nestedExtensionContext, null,
154+
doublyNestedClassDescriptor, configuration, extensionRegistry, null);
155+
// @formatter:off
156+
assertAll("doublyNestedContext",
157+
() -> assertThat(doublyNestedExtensionContext.getParent()).containsSame(nestedExtensionContext),
158+
() -> assertThat(doublyNestedExtensionContext.getTestClass()).contains(OuterClassTestCase.NestedClass.DoublyNestedClass.class),
159+
() -> assertThat(doublyNestedExtensionContext.getEnclosingTestClasses()).containsExactly(OuterClassTestCase.class, OuterClassTestCase.NestedClass.class)
160+
);
161+
// @formatter:on
162+
163+
var methodExtensionContext = new MethodExtensionContext(nestedExtensionContext, null, methodTestDescriptor,
164+
configuration, extensionRegistry, new OpenTest4JAwareThrowableCollector());
165+
// @formatter:off
166+
assertAll("methodContext",
167+
() -> assertThat(methodExtensionContext.getParent()).containsSame(nestedExtensionContext),
168+
() -> assertThat(methodExtensionContext.getTestClass()).contains(OuterClassTestCase.NestedClass.class),
169+
() -> assertThat(methodExtensionContext.getEnclosingTestClasses()).containsExactly(OuterClassTestCase.class)
170+
);
171+
// @formatter:on
141172
}
142173

143174
@Test
@@ -154,7 +185,6 @@ void ExtensionContext_With_ExtensionRegistry_getExtensions() {
154185
}
155186

156187
@Test
157-
@SuppressWarnings("resource")
158188
void tagsCanBeRetrievedInExtensionContext() {
159189
var nestedClassDescriptor = nestedClassDescriptor();
160190
var outerClassDescriptor = outerClassDescriptor(nestedClassDescriptor);
@@ -174,20 +204,19 @@ void tagsCanBeRetrievedInExtensionContext() {
174204

175205
var methodExtensionContext = new MethodExtensionContext(outerExtensionContext, null, methodTestDescriptor,
176206
configuration, extensionRegistry, new OpenTest4JAwareThrowableCollector());
177-
methodExtensionContext.setTestInstances(DefaultTestInstances.of(new OuterClass()));
207+
methodExtensionContext.setTestInstances(DefaultTestInstances.of(new OuterClassTestCase()));
178208
assertThat(methodExtensionContext.getTags()).containsExactlyInAnyOrder("outer-tag", "method-tag");
179209
assertThat(methodExtensionContext.getRoot()).isSameAs(outerExtensionContext);
180210
}
181211

182212
@Test
183-
@SuppressWarnings("resource")
184213
void fromMethodTestDescriptor() {
185214
var methodTestDescriptor = methodDescriptor();
186215
var classTestDescriptor = outerClassDescriptor(methodTestDescriptor);
187216
var engineDescriptor = new JupiterEngineDescriptor(UniqueId.forEngine("junit-jupiter"), configuration);
188217
engineDescriptor.addChild(classTestDescriptor);
189218

190-
Object testInstance = new OuterClass();
219+
Object testInstance = new OuterClassTestCase();
191220
var testMethod = methodTestDescriptor.getTestMethod();
192221

193222
var engineExtensionContext = new JupiterEngineExtensionContext(null, engineDescriptor, configuration,
@@ -201,10 +230,11 @@ void fromMethodTestDescriptor() {
201230
// @formatter:off
202231
assertAll("methodContext",
203232
() -> assertThat(methodExtensionContext.getElement()).contains(testMethod),
204-
() -> assertThat(methodExtensionContext.getTestClass()).contains(OuterClass.class),
233+
() -> assertThat(methodExtensionContext.getTestClass()).contains(OuterClassTestCase.class),
234+
() -> assertThat(methodExtensionContext.getEnclosingTestClasses()).isEmpty(),
205235
() -> assertThat(methodExtensionContext.getTestInstance()).contains(testInstance),
206236
() -> assertThat(methodExtensionContext.getTestMethod()).contains(testMethod),
207-
() -> assertThat(methodExtensionContext.getRequiredTestClass()).isEqualTo(OuterClass.class),
237+
() -> assertThat(methodExtensionContext.getRequiredTestClass()).isEqualTo(OuterClassTestCase.class),
208238
() -> assertThat(methodExtensionContext.getRequiredTestInstance()).isEqualTo(testInstance),
209239
() -> assertThat(methodExtensionContext.getRequiredTestMethod()).isEqualTo(testMethod),
210240
() -> assertThat(methodExtensionContext.getDisplayName()).isEqualTo(methodTestDescriptor.getDisplayName()),
@@ -359,7 +389,7 @@ void usingStore() {
359389
extensionRegistry, null);
360390
var childContext = new MethodExtensionContext(parentContext, null, methodTestDescriptor, configuration,
361391
extensionRegistry, new OpenTest4JAwareThrowableCollector());
362-
childContext.setTestInstances(DefaultTestInstances.of(new OuterClass()));
392+
childContext.setTestInstances(DefaultTestInstances.of(new OuterClassTestCase()));
363393

364394
var childStore = childContext.getStore(Namespace.GLOBAL);
365395
var parentStore = parentContext.getStore(Namespace.GLOBAL);
@@ -430,13 +460,18 @@ void configurationParameter(Function<JupiterConfiguration, ? extends ExtensionCo
430460
}
431461

432462
private NestedClassTestDescriptor nestedClassDescriptor() {
433-
return new NestedClassTestDescriptor(UniqueId.root("nested-class", "NestedClass"), OuterClass.NestedClass.class,
434-
List::of, configuration);
463+
return new NestedClassTestDescriptor(UniqueId.root("nested-class", "NestedClass"),
464+
OuterClassTestCase.NestedClass.class, List::of, configuration);
465+
}
466+
467+
private NestedClassTestDescriptor doublyNestedClassDescriptor() {
468+
return new NestedClassTestDescriptor(UniqueId.root("nested-class", "DoublyNestedClass"),
469+
OuterClassTestCase.NestedClass.DoublyNestedClass.class, List::of, configuration);
435470
}
436471

437472
private ClassTestDescriptor outerClassDescriptor(TestDescriptor child) {
438-
var classTestDescriptor = new ClassTestDescriptor(UniqueId.root("class", "OuterClass"), OuterClass.class,
439-
configuration);
473+
var classTestDescriptor = new ClassTestDescriptor(UniqueId.root("class", "OuterClass"),
474+
OuterClassTestCase.class, configuration);
440475
if (child != null) {
441476
classTestDescriptor.addChild(child);
442477
}
@@ -445,19 +480,41 @@ private ClassTestDescriptor outerClassDescriptor(TestDescriptor child) {
445480

446481
private TestMethodTestDescriptor methodDescriptor() {
447482
try {
448-
return new TestMethodTestDescriptor(UniqueId.root("method", "aMethod"), OuterClass.class,
449-
OuterClass.class.getDeclaredMethod("aMethod"), List::of, configuration);
483+
return new TestMethodTestDescriptor(UniqueId.root("method", "aMethod"), OuterClassTestCase.class,
484+
OuterClassTestCase.class.getDeclaredMethod("aMethod"), List::of, configuration);
450485
}
451486
catch (NoSuchMethodException e) {
452487
throw new RuntimeException(e);
453488
}
454489
}
455490

491+
private TestMethodTestDescriptor nestedMethodDescriptor() {
492+
try {
493+
return new TestMethodTestDescriptor(UniqueId.root("method", "nestedMethod"),
494+
OuterClassTestCase.NestedClass.class, BaseNestedTestCase.class.getDeclaredMethod("nestedMethod"),
495+
List::of, configuration);
496+
}
497+
catch (NoSuchMethodException e) {
498+
throw new RuntimeException(e);
499+
}
500+
}
501+
502+
static abstract class BaseNestedTestCase {
503+
@Test
504+
void nestedMethod() {
505+
}
506+
507+
@Nested
508+
class DoublyNestedClass {
509+
}
510+
}
511+
456512
@Tag("outer-tag")
457-
static class OuterClass {
513+
static class OuterClassTestCase {
458514

459515
@Tag("nested-tag")
460-
static class NestedClass {
516+
@Nested
517+
class NestedClass extends BaseNestedTestCase {
461518
}
462519

463520
@Tag("method-tag")

jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestExtensionTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.lang.reflect.Constructor;
2424
import java.lang.reflect.Method;
2525
import java.nio.file.Path;
26+
import java.util.List;
2627
import java.util.Map;
2728
import java.util.Optional;
2829
import java.util.Set;
@@ -251,6 +252,11 @@ public Optional<Class<?>> getTestClass() {
251252
return Optional.empty();
252253
}
253254

255+
@Override
256+
public List<Class<?>> getEnclosingTestClasses() {
257+
return List.of();
258+
}
259+
254260
@Override
255261
public Optional<Lifecycle> getTestInstanceLifecycle() {
256262
return Optional.empty();

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy