Skip to content

Commit 4276dd4

Browse files
committed
DisplayNameGenerator cannot access runtime enclosing type for @nested test class: #4130
1 parent ad0ef2e commit 4276dd4

File tree

12 files changed

+147
-13
lines changed

12 files changed

+147
-13
lines changed

junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGenerator.java

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import static org.junit.platform.commons.support.ModifierSupport.isStatic;
1616

1717
import java.lang.reflect.Method;
18+
import java.util.List;
1819
import java.util.Optional;
1920
import java.util.function.Predicate;
2021

@@ -89,7 +90,39 @@ public interface DisplayNameGenerator {
8990
* @param nestedClass the class to generate a name for; never {@code null}
9091
* @return the display name for the nested class; never blank
9192
*/
92-
String generateDisplayNameForNestedClass(Class<?> nestedClass);
93+
default String generateDisplayNameForNestedClass(Class<?> nestedClass) {
94+
throw new UnsupportedOperationException("Implement generateDisplayNameForNestedClass(List<Class<?>>, Class<?>) instead");
95+
}
96+
97+
/**
98+
* Generate a display name for the given {@link Nested @Nested} inner test class.
99+
*
100+
* <p>If it returns {@code null}, the default display name generator will be used instead.
101+
*
102+
* @param enclosingInstanceTypes concrete type of the enclosing instance for the nested class; never {@code null}
103+
* @param nestedClass the class to generate a name for; never {@code null}
104+
* @return the display name for the nested class; never blank
105+
*/
106+
default String generateDisplayNameForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> nestedClass) {
107+
return generateDisplayNameForNestedClass(nestedClass);
108+
}
109+
110+
/**
111+
* Generate a display name for the given method.
112+
*
113+
* <p>If it returns {@code null}, the default display name generator will be used instead.
114+
*
115+
* @implNote The class instance supplied as {@code testClass} may differ from
116+
* the class returned by {@code testMethod.getDeclaringClass()} &mdash; for
117+
* example, when a test method is inherited from a superclass.
118+
*
119+
* @param testClass the class the test method is invoked on; never {@code null}
120+
* @param testMethod method to generate a display name for; never {@code null}
121+
* @return the display name for the test; never blank
122+
*/
123+
default String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
124+
throw new UnsupportedOperationException("Implement generateDisplayNameForMethod(List<Class<?>>, Class<?>, Method) instead");
125+
}
93126

94127
/**
95128
* Generate a display name for the given method.
@@ -100,11 +133,15 @@ public interface DisplayNameGenerator {
100133
* the class returned by {@code testMethod.getDeclaringClass()} &mdash; for
101134
* example, when a test method is inherited from a superclass.
102135
*
136+
* @param enclosingInstanceTypes concrete types of the enclosing instance for the nested class; never {@code null}
103137
* @param testClass the class the test method is invoked on; never {@code null}
104138
* @param testMethod method to generate a display name for; never {@code null}
105139
* @return the display name for the test; never blank
106140
*/
107-
String generateDisplayNameForMethod(Class<?> testClass, Method testMethod);
141+
default String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
142+
Method testMethod) {
143+
return generateDisplayNameForMethod(testClass, testMethod);
144+
}
108145

109146
/**
110147
* Generate a string representation of the formal parameters of the supplied
@@ -243,17 +280,17 @@ public String generateDisplayNameForClass(Class<?> testClass) {
243280
}
244281

245282
@Override
246-
public String generateDisplayNameForNestedClass(Class<?> nestedClass) {
247-
return getSentenceBeginning(nestedClass);
283+
public String generateDisplayNameForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> nestedClass) {
284+
return getSentenceBeginning(enclosingInstanceTypes, nestedClass);
248285
}
249286

250287
@Override
251-
public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
252-
return getSentenceBeginning(testClass) + getFragmentSeparator(testClass)
253-
+ getGeneratorFor(testClass).generateDisplayNameForMethod(testClass, testMethod);
288+
public String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass, Method testMethod) {
289+
return getSentenceBeginning(enclosingInstanceTypes, testClass) + getFragmentSeparator(testClass)
290+
+ getGeneratorFor(testClass).generateDisplayNameForMethod(enclosingInstanceTypes, testClass, testMethod);
254291
}
255292

256-
private String getSentenceBeginning(Class<?> testClass) {
293+
private String getSentenceBeginning(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {
257294
Class<?> enclosingClass = testClass.getEnclosingClass();
258295
boolean topLevelTestClass = (enclosingClass == null || isStatic(testClass));
259296
Optional<String> displayName = findAnnotation(testClass, DisplayName.class)//
@@ -273,17 +310,19 @@ private String getSentenceBeginning(Class<?> testClass) {
273310
return generateDisplayNameForClass(testClass);
274311
}
275312

313+
Class<?> enclosingInstanceType = enclosingInstanceTypes.remove(enclosingInstanceTypes.size() - 1);
314+
276315
// Only build prefix based on the enclosing class if the enclosing
277316
// class is also configured to use the IndicativeSentences generator.
278-
boolean buildPrefix = findDisplayNameGeneration(enclosingClass)//
317+
boolean buildPrefix = findDisplayNameGeneration(enclosingInstanceType)//
279318
.map(DisplayNameGeneration::value)//
280319
.filter(IndicativeSentences.class::equals)//
281320
.isPresent();
282321

283-
String prefix = (buildPrefix ? getSentenceBeginning(enclosingClass) + getFragmentSeparator(testClass) : "");
322+
String prefix = (buildPrefix ? getSentenceBeginning(enclosingInstanceTypes, enclosingInstanceType) + getFragmentSeparator(testClass) : "");
284323

285324
return prefix + displayName.orElseGet(
286-
() -> getGeneratorFor(testClass).generateDisplayNameForNestedClass(testClass));
325+
() -> getGeneratorFor(testClass).generateDisplayNameForNestedClass(testClass));
287326
}
288327

289328
/**

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import java.lang.reflect.AnnotatedElement;
1616
import java.lang.reflect.Method;
17+
import java.util.List;
1718
import java.util.Optional;
1819
import java.util.function.Function;
1920
import java.util.function.Supplier;
@@ -95,6 +96,12 @@ static String determineDisplayNameForMethod(Class<?> testClass, Method testMetho
9596
createDisplayNameSupplierForMethod(testClass, testMethod, configuration));
9697
}
9798

99+
static String determineDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass, Method testMethod,
100+
JupiterConfiguration configuration) {
101+
return determineDisplayName(testMethod,
102+
createDisplayNameSupplierForMethod(enclosingInstanceTypes, testClass, testMethod, configuration));
103+
}
104+
98105
static Supplier<String> createDisplayNameSupplierForClass(Class<?> testClass, JupiterConfiguration configuration) {
99106
return createDisplayNameSupplier(testClass, configuration,
100107
generator -> generator.generateDisplayNameForClass(testClass));
@@ -106,12 +113,24 @@ static Supplier<String> createDisplayNameSupplierForNestedClass(Class<?> testCla
106113
generator -> generator.generateDisplayNameForNestedClass(testClass));
107114
}
108115

116+
static Supplier<String> createDisplayNameSupplierForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
117+
JupiterConfiguration configuration) {
118+
return createDisplayNameSupplier(testClass, configuration,
119+
generator -> generator.generateDisplayNameForNestedClass(enclosingInstanceTypes, testClass));
120+
}
121+
109122
private static Supplier<String> createDisplayNameSupplierForMethod(Class<?> testClass, Method testMethod,
110123
JupiterConfiguration configuration) {
111124
return createDisplayNameSupplier(testClass, configuration,
112125
generator -> generator.generateDisplayNameForMethod(testClass, testMethod));
113126
}
114127

128+
private static Supplier<String> createDisplayNameSupplierForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass, Method testMethod,
129+
JupiterConfiguration configuration) {
130+
return createDisplayNameSupplier(testClass, configuration,
131+
generator -> generator.generateDisplayNameForMethod(enclosingInstanceTypes, testClass, testMethod));
132+
}
133+
115134
private static Supplier<String> createDisplayNameSupplier(Class<?> testClass, JupiterConfiguration configuration,
116135
Function<DisplayNameGenerator, String> generatorFunction) {
117136
return () -> findDisplayNameGenerator(testClass) //

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ public abstract class MethodBasedTestDescriptor extends JupiterTestDescriptor im
6262
configuration);
6363
}
6464

65+
MethodBasedTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
66+
Method testMethod, JupiterConfiguration configuration) {
67+
this(uniqueId, determineDisplayNameForMethod(enclosingInstanceTypes, testClass, testMethod, configuration),
68+
testClass, testMethod, configuration);
69+
}
70+
6571
MethodBasedTestDescriptor(UniqueId uniqueId, String displayName, Class<?> testClass, Method testMethod,
6672
JupiterConfiguration configuration) {
6773
super(uniqueId, displayName, MethodSource.from(testClass, testMethod), configuration);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public NestedClassTestDescriptor(UniqueId uniqueId, Class<?> testClass, JupiterC
5050
super(uniqueId, testClass, createDisplayNameSupplierForNestedClass(testClass, configuration), configuration);
5151
}
5252

53+
public NestedClassTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes, Class<?> testClass, JupiterConfiguration configuration) {
54+
super(uniqueId, testClass, createDisplayNameSupplierForNestedClass(enclosingInstanceTypes, testClass, configuration), configuration);
55+
}
56+
5357
// --- TestDescriptor ------------------------------------------------------
5458

5559
@Override

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.lang.reflect.Method;
1919
import java.net.URI;
2020
import java.util.Iterator;
21+
import java.util.List;
2122
import java.util.Optional;
2223
import java.util.function.Supplier;
2324
import java.util.stream.Stream;
@@ -67,6 +68,11 @@ public TestFactoryTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method t
6768
super(uniqueId, testClass, testMethod, configuration);
6869
}
6970

71+
public TestFactoryTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
72+
Method testMethod, JupiterConfiguration configuration) {
73+
super(uniqueId, enclosingInstanceTypes, testClass, testMethod, configuration);
74+
}
75+
7076
// --- Filterable ----------------------------------------------------------
7177

7278
@Override

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import static org.junit.platform.commons.util.CollectionUtils.forEachInReverseOrder;
1818

1919
import java.lang.reflect.Method;
20+
import java.util.List;
2021

2122
import org.apiguardian.api.API;
2223
import org.junit.jupiter.api.TestInstance.Lifecycle;
@@ -80,6 +81,12 @@ public TestMethodTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method te
8081
this.interceptorCall = defaultInterceptorCall;
8182
}
8283

84+
public TestMethodTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
85+
Method testMethod, JupiterConfiguration configuration) {
86+
super(uniqueId, enclosingInstanceTypes, testClass, testMethod, configuration);
87+
this.interceptorCall = defaultInterceptorCall;
88+
}
89+
8390
TestMethodTestDescriptor(UniqueId uniqueId, String displayName, Class<?> testClass, Method testMethod,
8491
JupiterConfiguration configuration, ReflectiveInterceptorCall<Method, Void> interceptorCall) {
8592
super(uniqueId, displayName, testClass, testMethod, configuration);

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public TestTemplateTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method
5050
super(uniqueId, testClass, templateMethod, configuration);
5151
}
5252

53+
public TestTemplateTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
54+
Method templateMethod, JupiterConfiguration configuration) {
55+
super(uniqueId, enclosingInstanceTypes, testClass, templateMethod, configuration);
56+
}
57+
5358
// --- Filterable ----------------------------------------------------------
5459

5560
@Override

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/ClassSelectorResolver.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ else if (isNestedTestClass.test(testClass)) {
8383
public Resolution resolve(NestedClassSelector selector, Context context) {
8484
if (isNestedTestClass.test(selector.getNestedClass())) {
8585
return toResolution(context.addToParent(() -> selectClass(selector.getEnclosingClasses()),
86-
parent -> Optional.of(newNestedClassTestDescriptor(parent, selector.getNestedClass()))));
86+
parent -> Optional.of(newNestedClassTestDescriptor(parent, selector.getEnclosingClasses(), selector.getNestedClass()))));
8787
}
8888
return unresolved();
8989
}
@@ -127,6 +127,12 @@ private NestedClassTestDescriptor newNestedClassTestDescriptor(TestDescriptor pa
127127
configuration);
128128
}
129129

130+
private NestedClassTestDescriptor newNestedClassTestDescriptor(TestDescriptor parent, List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {
131+
return new NestedClassTestDescriptor(
132+
parent.getUniqueId().append(NestedClassTestDescriptor.SEGMENT_TYPE, testClass.getSimpleName()), enclosingInstanceTypes, testClass,
133+
configuration);
134+
}
135+
130136
private Resolution toResolution(Optional<? extends ClassBasedTestDescriptor> testDescriptor) {
131137
return testDescriptor.map(it -> {
132138
Class<?> testClass = it.getTestClass();

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodSelectorResolver.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,12 @@ protected TestDescriptor createTestDescriptor(UniqueId uniqueId, Class<?> testCl
169169
JupiterConfiguration configuration) {
170170
return new TestMethodTestDescriptor(uniqueId, testClass, method, configuration);
171171
}
172+
173+
@Override
174+
protected TestDescriptor createTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes,
175+
Class<?> testClass, Method method, JupiterConfiguration configuration) {
176+
return new TestMethodTestDescriptor(uniqueId, enclosingInstanceTypes, testClass, method, configuration);
177+
}
172178
},
173179

174180
TEST_FACTORY(new IsTestFactoryMethod(), TestFactoryTestDescriptor.SEGMENT_TYPE,
@@ -179,6 +185,12 @@ protected TestDescriptor createTestDescriptor(UniqueId uniqueId, Class<?> testCl
179185
JupiterConfiguration configuration) {
180186
return new TestFactoryTestDescriptor(uniqueId, testClass, method, configuration);
181187
}
188+
189+
@Override
190+
protected TestDescriptor createTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes,
191+
Class<?> testClass, Method method, JupiterConfiguration configuration) {
192+
return new TestFactoryTestDescriptor(uniqueId, enclosingInstanceTypes, testClass, method, configuration);
193+
}
182194
},
183195

184196
TEST_TEMPLATE(new IsTestTemplateMethod(), TestTemplateTestDescriptor.SEGMENT_TYPE,
@@ -188,6 +200,12 @@ protected TestDescriptor createTestDescriptor(UniqueId uniqueId, Class<?> testCl
188200
JupiterConfiguration configuration) {
189201
return new TestTemplateTestDescriptor(uniqueId, testClass, method, configuration);
190202
}
203+
204+
@Override
205+
protected TestDescriptor createTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes,
206+
Class<?> testClass, Method method, JupiterConfiguration configuration) {
207+
return new TestTemplateTestDescriptor(uniqueId, enclosingInstanceTypes, testClass, method, configuration);
208+
}
191209
};
192210

193211
private final Predicate<Method> methodPredicate;
@@ -207,7 +225,7 @@ private Optional<TestDescriptor> resolve(List<Class<?>> enclosingClasses, Class<
207225
}
208226
return context.addToParent(() -> selectClass(enclosingClasses, testClass), //
209227
parent -> Optional.of(
210-
createTestDescriptor(createUniqueId(method, parent), testClass, method, configuration)));
228+
createTestDescriptor(createUniqueId(method, parent), enclosingClasses, testClass, method, configuration)));
211229
}
212230

213231
private DiscoverySelector selectClass(List<Class<?>> enclosingClasses, Class<?> testClass) {
@@ -246,6 +264,9 @@ private UniqueId createUniqueId(Method method, TestDescriptor parent) {
246264
protected abstract TestDescriptor createTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method method,
247265
JupiterConfiguration configuration);
248266

267+
protected abstract TestDescriptor createTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes,
268+
Class<?> testClass, Method method, JupiterConfiguration configuration);
269+
249270
}
250271

251272
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.junit.jupiter.api;
2+
3+
@IndicativeSentencesGeneration
4+
abstract class AbstractBaseTests {
5+
6+
@Nested
7+
class NestedTests {
8+
9+
@Test
10+
void test() {
11+
}
12+
}
13+
}

0 commit comments

Comments
 (0)