From 9b15eddda33047b755116a6c154dacbd1b6ad014 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 6 Jul 2016 20:58:12 +0200 Subject: [PATCH] Introduce @UseTechnicalNames for the JUnitPlatform runner 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 --- .../docs/asciidoc/release-notes-5.0.0-M1.adoc | 2 + .../src/docs/asciidoc/running-tests.adoc | 19 ++++-- .../runner/JUnitPlatformTestTree.java | 61 +++++++++++-------- .../platform/runner/UseTechnicalNames.java | 47 ++++++++++++++ .../runner/JUnitPlatformRunnerTests.java | 30 +++++++++ 5 files changed, 131 insertions(+), 28 deletions(-) create mode 100644 junit-platform-runner/src/main/java/org/junit/platform/runner/UseTechnicalNames.java diff --git a/documentation/src/docs/asciidoc/release-notes-5.0.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes-5.0.0-M1.adoc index 71cfe2d7501..23444e2494b 100644 --- a/documentation/src/docs/asciidoc/release-notes-5.0.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes-5.0.0-M1.adoc @@ -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 <>. * 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`. diff --git a/documentation/src/docs/asciidoc/running-tests.adoc b/documentation/src/docs/asciidoc/running-tests.adoc index 3627eac4c72..2582b2b010f 100644 --- a/documentation/src/docs/asciidoc/running-tests.adoc +++ b/documentation/src/docs/asciidoc/running-tests.adoc @@ -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 <>). But for the +time being the `JUnitPlatform` runner is an easy way to get started. [[running-tests-junit-platform-runner-setup]] ==== Setup @@ -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 diff --git a/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformTestTree.java b/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformTestTree.java index 289d005367f..e0aec406184 100644 --- a/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformTestTree.java +++ b/junit-platform-runner/src/main/java/org/junit/platform/runner/JUnitPlatformTestTree.java @@ -38,10 +38,12 @@ class JUnitPlatformTestTree { private final Map 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); } @@ -79,16 +81,27 @@ 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 optionalSource = testIdentifier.getSource(); if (optionalSource.isPresent()) { TestSource source = optionalSource.get(); @@ -96,23 +109,19 @@ private String getName(TestIdentifier testIdentifier) { return ((JavaClassSource) source).getJavaClass().getName(); } else if (source instanceof JavaMethodSource) { - JavaMethodSource javaMethodSource = (JavaMethodSource) source; - List> 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(""); + // @formatter:on } - private String getClassName(TestIdentifier testIdentifier, TestPlan testPlan) { + private String getTechnicalName(TestIdentifier testIdentifier) { Optional optionalSource = testIdentifier.getSource(); if (optionalSource.isPresent()) { TestSource source = optionalSource.get(); @@ -120,16 +129,20 @@ private String getClassName(TestIdentifier testIdentifier, TestPlan testPlan) { return ((JavaClassSource) source).getJavaClass().getName(); } else if (source instanceof JavaMethodSource) { - return ((JavaMethodSource) source).getJavaClass().getName(); + JavaMethodSource javaMethodSource = (JavaMethodSource) source; + List> 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(""); - // @formatter:on + // Else fall back to display name + return testIdentifier.getDisplayName(); } Set getTestsInSubtree(TestIdentifier ancestor) { diff --git a/junit-platform-runner/src/main/java/org/junit/platform/runner/UseTechnicalNames.java b/junit-platform-runner/src/main/java/org/junit/platform/runner/UseTechnicalNames.java new file mode 100644 index 00000000000..ce7b06a9606 --- /dev/null +++ b/junit-platform-runner/src/main/java/org/junit/platform/runner/UseTechnicalNames.java @@ -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 technical names should be + * used instead of display names when running a test suite via + * {@code @RunWith(JUnitPlatform.class)}. + * + *

By default, display names 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 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, 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 { +} diff --git a/platform-tests/src/test/java/org/junit/platform/runner/JUnitPlatformRunnerTests.java b/platform-tests/src/test/java/org/junit/platform/runner/JUnitPlatformRunnerTests.java index 8c4ecfdf643..100a4bd5db7 100644 --- a/platform-tests/src/test/java/org/junit/platform/runner/JUnitPlatformRunnerTests.java +++ b/platform-tests/src/test/java/org/junit/platform/runner/JUnitPlatformRunnerTests.java @@ -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 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); @@ -449,6 +475,10 @@ private LauncherDiscoveryRequest instantiateRunnerAndCaptureGeneratedRequest(Cla private static class TestClass { } + @UseTechnicalNames + private static class TestClassWithTechnicalNames { + } + private static class DynamicTestEngine implements TestEngine { @Override