Skip to content

Commit f7b93a3

Browse files
committed
Use real names and class names in JUnitPlatform runner
Prior to this commit, display names were used as the `name` and `className` in Descriptions created by the JUnitPlatform runner. This behavior had a coolness factor in that emoji and special characters would be displayed in IDEs and test reports. More importantly, however, it had a huge negative side effect in that such reports were not hierarchically structured. Specifically, all test classes appeared to be in the "default" package, which is not very useful for tooling or analysis. Consequently, this commit now uses real method names and class names in Descriptions for "tests" created by the JUnitPlatform runner if the source of the test is a Java method. This commit does not alter the existing support for "suites" (i.e., test containers) or test sources other than Java methods; in those cases, the display name is still used. Issue: #372
1 parent 26e23f9 commit f7b93a3

File tree

4 files changed

+113
-2
lines changed

4 files changed

+113
-2
lines changed

junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformTestTree.java

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,17 @@
1515

1616
import java.util.HashMap;
1717
import java.util.LinkedHashSet;
18+
import java.util.List;
1819
import java.util.Map;
1920
import java.util.Map.Entry;
21+
import java.util.Optional;
2022
import java.util.Set;
2123
import java.util.function.Predicate;
2224

25+
import org.junit.platform.commons.util.StringUtils;
26+
import org.junit.platform.engine.TestSource;
27+
import org.junit.platform.engine.support.descriptor.JavaClassSource;
28+
import org.junit.platform.engine.support.descriptor.JavaMethodSource;
2329
import org.junit.platform.launcher.TestIdentifier;
2430
import org.junit.platform.launcher.TestPlan;
2531
import org.junit.runner.Description;
@@ -73,14 +79,59 @@ private void buildDescription(TestIdentifier identifier, Description parent, Tes
7379

7480
private Description createJUnit4Description(TestIdentifier identifier, TestPlan testPlan) {
7581
if (identifier.isTest()) {
76-
return Description.createTestDescription(testPlan.getParent(identifier).orElse(identifier).getDisplayName(),
77-
identifier.getDisplayName(), identifier.getUniqueId());
82+
String className = getClassName(identifier, testPlan);
83+
String name = getName(identifier);
84+
return Description.createTestDescription(className, name, identifier.getUniqueId());
7885
}
7986
else {
8087
return Description.createSuiteDescription(identifier.getDisplayName(), identifier.getUniqueId());
8188
}
8289
}
8390

91+
private String getName(TestIdentifier testIdentifier) {
92+
Optional<TestSource> optionalSource = testIdentifier.getSource();
93+
if (optionalSource.isPresent()) {
94+
TestSource source = optionalSource.get();
95+
if (source instanceof JavaClassSource) {
96+
return ((JavaClassSource) source).getJavaClass().getName();
97+
}
98+
else if (source instanceof JavaMethodSource) {
99+
JavaMethodSource javaMethodSource = (JavaMethodSource) source;
100+
List<Class<?>> parameterTypes = javaMethodSource.getJavaMethodParameterTypes();
101+
if (parameterTypes.size() == 0) {
102+
return javaMethodSource.getJavaMethodName();
103+
}
104+
else {
105+
return String.format("%s(%s)", javaMethodSource.getJavaMethodName(), StringUtils.nullSafeToString(
106+
Class::getName, parameterTypes.toArray(new Class<?>[parameterTypes.size()])));
107+
}
108+
}
109+
}
110+
111+
// Else fall back to display name
112+
return testIdentifier.getDisplayName();
113+
}
114+
115+
private String getClassName(TestIdentifier testIdentifier, TestPlan testPlan) {
116+
Optional<TestSource> optionalSource = testIdentifier.getSource();
117+
if (optionalSource.isPresent()) {
118+
TestSource source = optionalSource.get();
119+
if (source instanceof JavaClassSource) {
120+
return ((JavaClassSource) source).getJavaClass().getName();
121+
}
122+
else if (source instanceof JavaMethodSource) {
123+
return ((JavaMethodSource) source).getJavaClass().getName();
124+
}
125+
}
126+
127+
// Else fall back to display name of parent
128+
// @formatter:off
129+
return testPlan.getParent(testIdentifier)
130+
.map(TestIdentifier::getDisplayName)
131+
.orElse("<unrooted>");
132+
// @formatter:on
133+
}
134+
84135
Set<TestIdentifier> getTestsInSubtree(TestIdentifier ancestor) {
85136
// @formatter:off
86137
return plan.getDescendants(ancestor).stream()

platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/DummyTestDescriptor.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import static org.junit.platform.engine.support.hierarchical.Node.SkipResult.doNotSkip;
1414
import static org.junit.platform.engine.support.hierarchical.Node.SkipResult.skip;
1515

16+
import org.junit.platform.engine.TestSource;
1617
import org.junit.platform.engine.UniqueId;
1718
import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;
1819

@@ -26,7 +27,14 @@ public class DummyTestDescriptor extends AbstractTestDescriptor implements Node<
2627
private boolean skipped;
2728

2829
DummyTestDescriptor(UniqueId uniqueId, String displayName, Runnable runnable) {
30+
this(uniqueId, displayName, null, runnable);
31+
}
32+
33+
DummyTestDescriptor(UniqueId uniqueId, String displayName, TestSource source, Runnable runnable) {
2934
super(uniqueId, displayName);
35+
if (source != null) {
36+
setSource(source);
37+
}
3038
this.runnable = runnable;
3139
}
3240

platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/DummyTestEngine.java

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

1111
package org.junit.platform.engine.support.hierarchical;
1212

13+
import java.lang.reflect.Method;
14+
1315
import org.junit.platform.engine.EngineDiscoveryRequest;
1416
import org.junit.platform.engine.ExecutionRequest;
1517
import org.junit.platform.engine.TestDescriptor;
1618
import org.junit.platform.engine.UniqueId;
19+
import org.junit.platform.engine.support.descriptor.JavaMethodSource;
1720

1821
/**
1922
* @since 1.0
@@ -45,6 +48,14 @@ public DummyTestDescriptor addTest(String uniqueName, Runnable runnable) {
4548
return addTest(uniqueName, uniqueName, runnable);
4649
}
4750

51+
public DummyTestDescriptor addTest(Method testMethod, Runnable runnable) {
52+
UniqueId uniqueId = engineDescriptor.getUniqueId().append("test", testMethod.getName());
53+
JavaMethodSource source = new JavaMethodSource(testMethod);
54+
DummyTestDescriptor child = new DummyTestDescriptor(uniqueId, testMethod.getName(), source, runnable);
55+
engineDescriptor.addChild(child);
56+
return child;
57+
}
58+
4859
public DummyTestDescriptor addTest(String uniqueName, String displayName, Runnable runnable) {
4960
UniqueId uniqueId = engineDescriptor.getUniqueId().append("test", uniqueName);
5061
DummyTestDescriptor child = new DummyTestDescriptor(uniqueId, displayName, runnable);
@@ -61,4 +72,5 @@ public TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId
6172
protected DummyEngineExecutionContext createExecutionContext(ExecutionRequest request) {
6273
return new DummyEngineExecutionContext();
6374
}
75+
6476
}

platform-tests/src/test/java/org/junit/platform/runner/JUnitPlatformRunnerTests.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import static java.util.Collections.singleton;
1616
import static org.assertj.core.api.Assertions.assertThat;
1717
import static org.assertj.core.api.Assertions.fail;
18+
import static org.junit.jupiter.api.Assertions.assertAll;
1819
import static org.junit.jupiter.api.Assertions.assertEquals;
1920
import static org.junit.jupiter.api.Assertions.assertThrows;
2021
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -34,6 +35,8 @@
3435
import static org.mockito.Mockito.verifyNoMoreInteractions;
3536
import static org.mockito.Mockito.when;
3637

38+
import java.lang.reflect.Method;
39+
import java.util.ArrayList;
3740
import java.util.List;
3841

3942
import org.junit.jupiter.api.Nested;
@@ -381,6 +384,43 @@ void reportsIgnoredEventsForLeavesWhenContainerIsSkipped() throws Exception {
381384

382385
}
383386

387+
@Nested
388+
class Descriptions {
389+
390+
@Test
391+
void descriptionForJavaMethodSource() throws Exception {
392+
DummyTestEngine engine = new DummyTestEngine("dummy");
393+
Method failingTest = getClass().getDeclaredMethod("failingTest");
394+
engine.addTest(failingTest, () -> {
395+
});
396+
397+
JUnitPlatform platformRunner = new JUnitPlatform(TestClass.class, createLauncher(engine));
398+
399+
ArrayList<Description> children = platformRunner.getDescription().getChildren();
400+
assertEquals(1, children.size());
401+
Description engineDescription = children.get(0);
402+
assertEquals("dummy", engineDescription.getDisplayName());
403+
404+
children = engineDescription.getChildren();
405+
assertEquals(1, children.size());
406+
Description testDescription = children.get(0);
407+
// @formatter:off
408+
assertAll(
409+
() -> assertEquals(getClass().getName(), testDescription.getClassName(), "class name"),
410+
() -> assertEquals("failingTest", testDescription.getMethodName(), "method name"),
411+
() -> assertEquals("failingTest(" + getClass().getName() + ")", testDescription.getDisplayName(), "display name")
412+
);
413+
// @formatter:on
414+
}
415+
416+
void failingTest() {
417+
// not actually invoked
418+
}
419+
420+
}
421+
422+
// -------------------------------------------------------------------------
423+
384424
private static Description suiteDescription(String uniqueId) {
385425
return createSuiteDescription(uniqueId, uniqueId);
386426
}

0 commit comments

Comments
 (0)