Skip to content

Commit

Permalink
Introduce @UseTechnicalNames for the JUnitPlatform runner
Browse files Browse the repository at this point in the history
Due to recent changes, technical names were previously used for test
methods and their test classes instead of display names.

This commit introduces a new @UseTechnicalNames annotation that allows
one to opt-in for the usage of technical names instead of display
names. If @UseTechnicalNames not directly present on the test class run
via the JUnitPlatform runner, display names will be used by default.

Closes #379
  • Loading branch information
sbrannen committed Jul 6, 2016
1 parent 04a0b7f commit 9b15edd
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 28 deletions.
2 changes: 2 additions & 0 deletions documentation/src/docs/asciidoc/release-notes-5.0.0-M1.adoc
Expand Up @@ -74,6 +74,8 @@ Platform, Jupiter, and Vintage, consult the dedicated subsections.
** `@Packages` has been renamed to `@SelectPackages`.
** `@Classes` has been renamed to `@SelectClasses`.
** `@UniqueIds` has been removed.
** `@UseTechnicalNames` has been introduced.
*** See <<running-tests-junit-platform-runner-technical-names>>.
* The Gradle plugin for the JUnit Platform has been completely overhauled.
** The JUnit Platform Gradle plugin now requires Gradle 2.5 or higher.
** The `junit5Test` Gradle task has been renamed to `junitPlatformTest`.
Expand Down
19 changes: 15 additions & 4 deletions documentation/src/docs/asciidoc/running-tests.adoc
Expand Up @@ -297,10 +297,10 @@ for example, a JUnit Jupiter test class.
Annotating a class with `@RunWith(JUnitPlatform.class)` allows it to be run with IDEs and
build systems that support JUnit 4 but do not yet support the JUnit Platform directly.

NOTE: Since the JUnit Platform has features that JUnit 4 does not have, the runner is only able
to support a subset of the JUnit Platform functionality, especially with regard to
reporting. But for the time being the `JUnitPlatform` runner is an easy way to get
started.
NOTE: Since the JUnit Platform has features that JUnit 4 does not have, the runner is
only able to support a subset of the JUnit Platform functionality, especially with regard
to reporting (see <<running-tests-junit-platform-runner-technical-names>>). But for the
time being the `JUnitPlatform` runner is an easy way to get started.

[[running-tests-junit-platform-runner-setup]]
==== Setup
Expand All @@ -313,6 +313,17 @@ versions.
* `junit-platform-runner` in _test_ scope: location of the `JUnitPlatform` runner
* `junit-jupiter-engine` in _test runtime_ scope: implementation of the Engine API for JUnit Jupiter

[[running-tests-junit-platform-runner-technical-names]]
==== Display Names vs. Technical Names

By default, _display names_ will be used for test artifacts; however, when the
`JUnitPlatform` runner is used to execute tests with a build tool such as Gradle or
Maven, the generated test report often needs to include the _technical names_ of test
artifacts — for example, fully qualified class names — instead of shorter display names
like the simple name of a test class or a custom display name containing special
characters. To enable technical names for reporting purposes, simply declare the
`@UseTechnicalNames` annotation alongside `@RunWith(JUnitPlatform.class)`.

[[running-tests-junit-platform-runner-single-test]]
==== Single Test Class

Expand Down
Expand Up @@ -38,10 +38,12 @@ class JUnitPlatformTestTree {

private final Map<TestIdentifier, Description> descriptions = new HashMap<>();
private final TestPlan plan;
private final boolean useTechnicalNames;
private final Description suiteDescription;

JUnitPlatformTestTree(TestPlan plan, Class<?> testClass) {
this.plan = plan;
this.useTechnicalNames = testClass.isAnnotationPresent(UseTechnicalNames.class);
this.suiteDescription = generateSuiteDescription(plan, testClass);
}

Expand Down Expand Up @@ -79,57 +81,68 @@ private void buildDescription(TestIdentifier identifier, Description parent, Tes

private Description createJUnit4Description(TestIdentifier identifier, TestPlan testPlan) {
if (identifier.isTest()) {
String className = getClassName(identifier, testPlan);
String name = getName(identifier);
return Description.createTestDescription(className, name, identifier.getUniqueId());
String containerName;
String name;

if (this.useTechnicalNames) {
containerName = getTechnicalContainerName(identifier, testPlan);
name = getTechnicalName(identifier);
}
else {
containerName = testPlan.getParent(identifier).orElse(identifier).getDisplayName();
name = identifier.getDisplayName();
}

return Description.createTestDescription(containerName, name, identifier.getUniqueId());
}
else {
return Description.createSuiteDescription(identifier.getDisplayName(), identifier.getUniqueId());
String name = (this.useTechnicalNames ? getTechnicalName(identifier) : identifier.getDisplayName());
return Description.createSuiteDescription(name, identifier.getUniqueId());
}
}

private String getName(TestIdentifier testIdentifier) {
private String getTechnicalContainerName(TestIdentifier testIdentifier, TestPlan testPlan) {
Optional<TestSource> optionalSource = testIdentifier.getSource();
if (optionalSource.isPresent()) {
TestSource source = optionalSource.get();
if (source instanceof JavaClassSource) {
return ((JavaClassSource) source).getJavaClass().getName();
}
else if (source instanceof JavaMethodSource) {
JavaMethodSource javaMethodSource = (JavaMethodSource) source;
List<Class<?>> parameterTypes = javaMethodSource.getJavaMethodParameterTypes();
if (parameterTypes.size() == 0) {
return javaMethodSource.getJavaMethodName();
}
else {
return String.format("%s(%s)", javaMethodSource.getJavaMethodName(), StringUtils.nullSafeToString(
Class::getName, parameterTypes.toArray(new Class<?>[parameterTypes.size()])));
}
return ((JavaMethodSource) source).getJavaClass().getName();
}
}

// Else fall back to display name
return testIdentifier.getDisplayName();
// Else fall back to display name of parent
// @formatter:off
return testPlan.getParent(testIdentifier)
.map(TestIdentifier::getDisplayName)
.orElse("<unrooted>");
// @formatter:on
}

private String getClassName(TestIdentifier testIdentifier, TestPlan testPlan) {
private String getTechnicalName(TestIdentifier testIdentifier) {
Optional<TestSource> optionalSource = testIdentifier.getSource();
if (optionalSource.isPresent()) {
TestSource source = optionalSource.get();
if (source instanceof JavaClassSource) {
return ((JavaClassSource) source).getJavaClass().getName();
}
else if (source instanceof JavaMethodSource) {
return ((JavaMethodSource) source).getJavaClass().getName();
JavaMethodSource javaMethodSource = (JavaMethodSource) source;
List<Class<?>> parameterTypes = javaMethodSource.getJavaMethodParameterTypes();
if (parameterTypes.size() == 0) {
return javaMethodSource.getJavaMethodName();
}
else {
return String.format("%s(%s)", javaMethodSource.getJavaMethodName(), StringUtils.nullSafeToString(
Class::getName, parameterTypes.toArray(new Class<?>[parameterTypes.size()])));
}
}
}

// Else fall back to display name of parent
// @formatter:off
return testPlan.getParent(testIdentifier)
.map(TestIdentifier::getDisplayName)
.orElse("<unrooted>");
// @formatter:on
// Else fall back to display name
return testIdentifier.getDisplayName();
}

Set<TestIdentifier> getTestsInSubtree(TestIdentifier ancestor) {
Expand Down
@@ -0,0 +1,47 @@
/*
* Copyright 2015-2016 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/

package org.junit.platform.runner;

import static org.junit.platform.commons.meta.API.Usage.Maintained;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.platform.commons.meta.API;

/**
* {@code @UseTechnicalNames} specifies that <em>technical names</em> should be
* used instead of <em>display names</em> when running a test suite via
* {@code @RunWith(JUnitPlatform.class)}.
*
* <p>By default, <em>display names</em> will be used for test artifacts; however,
* when the {@link JUnitPlatform} runner is used to execute tests with a build
* tool such as Gradle or Maven, the generated test report often needs to include
* the <em>technical names</em> of test artifacts &mdash; for example, fully
* qualified class names &mdash; instead of shorter <em>display names</em> like
* the simple name of a test class or a custom display name containing special
* characters. To enable <em>technical names</em>, simply declare the
* {@code @UseTechnicalNames} annotation alongside {@code @RunWith(JUnitPlatform.class)}.
*
* @since 1.0
* @see JUnitPlatform
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@Documented
@API(Maintained)
public @interface UseTechnicalNames {
}
Expand Up @@ -401,6 +401,32 @@ void descriptionForJavaMethodSource() throws Exception {
Description engineDescription = children.get(0);
assertEquals("dummy", engineDescription.getDisplayName());

children = engineDescription.getChildren();
assertEquals(1, children.size());
Description testDescription = children.get(0);
// @formatter:off
assertAll(
() -> assertEquals("dummy", testDescription.getClassName(), "class name"),
() -> assertEquals("failingTest", testDescription.getMethodName(), "method name"),
() -> assertEquals("failingTest(dummy)", testDescription.getDisplayName(), "display name")
);
// @formatter:on
}

@Test
void descriptionForJavaMethodSourceUsingTechnicalNames() throws Exception {
DummyTestEngine engine = new DummyTestEngine("dummy");
Method failingTest = getClass().getDeclaredMethod("failingTest");
engine.addTest(failingTest, () -> {
});

JUnitPlatform platformRunner = new JUnitPlatform(TestClassWithTechnicalNames.class, createLauncher(engine));

ArrayList<Description> children = platformRunner.getDescription().getChildren();
assertEquals(1, children.size());
Description engineDescription = children.get(0);
assertEquals("dummy", engineDescription.getDisplayName());

children = engineDescription.getChildren();
assertEquals(1, children.size());
Description testDescription = children.get(0);
Expand Down Expand Up @@ -449,6 +475,10 @@ private LauncherDiscoveryRequest instantiateRunnerAndCaptureGeneratedRequest(Cla
private static class TestClass {
}

@UseTechnicalNames
private static class TestClassWithTechnicalNames {
}

private static class DynamicTestEngine implements TestEngine {

@Override
Expand Down

0 comments on commit 9b15edd

Please sign in to comment.