Skip to content

Commit

Permalink
Introduce DisplayName generation SPI
Browse files Browse the repository at this point in the history
Addresses #162
  • Loading branch information
sormuras committed Sep 21, 2018
1 parent 597b2d1 commit 8f18466
Show file tree
Hide file tree
Showing 9 changed files with 549 additions and 24 deletions.
66 changes: 66 additions & 0 deletions documentation/src/test/java/example/DisplayNameGeneratorDemo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2015-2018 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 v2.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v20.html
*/

package example;

import java.lang.reflect.Method;

import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGeneration.Style;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

@DisplayNameGeneration(Style.DEFAULT)
class DisplayNameGeneratorDemo {

@Nested
@DisplayNameGeneration(Style.UNDERSCORE)
class A_year_is_not_supported {

@Test
void if_it_is_0() {
}

@ParameterizedTest(name = "For example, year {0} is not supported.")
@ValueSource(ints = { -1, -4 })
void if_it_is_negative(int year) {
}
}

@Nested
@DisplayNameGeneration(generator = Shout.class)
class A_year_is_a_leap_year {

@Test
void if_it_is_divisible_by_4_but_not_by_100() {
}
}

static class Shout implements DisplayNameGenerator {

@Override
public String generateDisplayNameForClass(Class<?> testClass) {
return Style.DEFAULT.generateDisplayNameForClass(testClass);
}

@Override
public String generateDisplayNameForNestedClass(Class<?> nestedClass) {
return nestedClass.getSimpleName().toUpperCase().replace('_', ' ') + "!";
}

@Override
public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
return testMethod.getName().toUpperCase().replace('_', ' ') + "?!";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
* @see Test
* @see Tag
* @see TestInfo
* @see DisplayNameGeneration
* @see DisplayNameGenerator
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright 2015-2018 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 v2.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v20.html
*/

package org.junit.jupiter.api;

import static org.apiguardian.api.API.Status.EXPERIMENTAL;

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 java.lang.reflect.Method;

import org.apiguardian.api.API;
import org.junit.platform.commons.util.ClassUtils;
import org.junit.platform.commons.util.Preconditions;

/**
* {@code @DisplayNameGeneration} is used to declare...
*
* <p>Display names are typically used for test reporting in IDEs and build
* tools and may contain spaces, special characters, and even emoji.
*
* @since 5.4
* @see DisplayName
* @see DisplayNameGenerator
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@API(status = EXPERIMENTAL, since = "5.4")
public @interface DisplayNameGeneration {

/**
* @return custom display name generator implementation or {@link DisplayNameGenerator}
* to use the {@code Style} provided by the {@link #value()} property
*/
Class<? extends DisplayNameGenerator> generator() default DisplayNameGenerator.class;

/**
* @return the style to use, can be overridden by a custom display name generator implementation
*/
Style value() default Style.DEFAULT;

/**
* TODO Javadoc
*/
enum Style implements DisplayNameGenerator {
/**
* Default display name generator.
*/
DEFAULT {
/**
* TODO Javadoc
*/
@Override
public String generateDisplayNameForClass(Class<?> testClass) {
Preconditions.notNull(testClass, "Test class must not be null");
String name = testClass.getName();
int lastDot = name.lastIndexOf('.');
return name.substring(lastDot + 1);
}

/**
* TODO Javadoc
*/
@Override
public String generateDisplayNameForNestedClass(Class<?> nestedClass) {
Preconditions.notNull(nestedClass, "Nested test class must not be null");
return nestedClass.getSimpleName();
}

/**
* TODO Javadoc
*/
@Override
public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
Preconditions.notNull(testClass, "Test class must not be null");
Preconditions.notNull(testMethod, "Test method must not be null");
return testMethod.getName() + parameterTypesAsString(testMethod);
}
},

/**
* TODO Javadoc
*/
UNDERSCORE {
/**
* TODO Javadoc
*/
@Override
public String generateDisplayNameForClass(Class<?> testClass) {
return replaceUnderscore(DEFAULT.generateDisplayNameForClass(testClass));
}

/**
* TODO Javadoc
*/
@Override
public String generateDisplayNameForNestedClass(Class<?> nestedClass) {
return replaceUnderscore(DEFAULT.generateDisplayNameForNestedClass(nestedClass));
}

/**
* TODO Javadoc
*/
@Override
public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
Preconditions.notNull(testClass, "Test class must not be null");
Preconditions.notNull(testMethod, "Test method must not be null");
return replaceUnderscore(testMethod.getName()) + parameterTypesAsString(testMethod);
}

private String replaceUnderscore(String name) {
return name.replace('_', ' ');
}
};

/**
* @return a string representation of all parameter types of the
* passed method or {@code "()"} if the method has no parameters
*/
private static String parameterTypesAsString(Method testMethod) {
return '(' + ClassUtils.nullSafeToString(Class::getSimpleName, testMethod.getParameterTypes()) + ')';
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2015-2018 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 v2.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v20.html
*/

package org.junit.jupiter.api;

import static org.apiguardian.api.API.Status.EXPERIMENTAL;

import java.lang.reflect.Method;

import org.apiguardian.api.API;

/**
* {@code DisplayNameGenerator} defines the SPI for generating display
* names programmatically.
*
* @since 5.4
* @see DisplayName
* @see DisplayNameGeneration
*/
@API(status = EXPERIMENTAL, since = "5.4")
public interface DisplayNameGenerator {

/**
* Generate a display name for the given top-level test class.
*
* TODO Javadoc
*/
String generateDisplayNameForClass(Class<?> testClass);

/**
* Generate a display name for the given nested test class.
*
* TODO Javadoc
* TODO Find better name, split into two different methods?
*/
String generateDisplayNameForNestedClass(Class<?> nestedClass);

/**
* Generate a display name for the given method.
*
* TODO Javadoc
*
* @implNote The class instance passed as {@code testClass} may differ from
* the returned class by {@code testMethod.getDeclaringClass()}: e.g., when
* a test method is inherited from a super class.
*
* @param testClass current test class this method "belongs" to
* @param testMethod method to generate a display name for
*/
String generateDisplayNameForMethod(Class<?> testClass, Method testMethod);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;

import org.apiguardian.api.API;
import org.junit.jupiter.api.TestInstance.Lifecycle;
Expand All @@ -49,7 +50,6 @@
import org.junit.jupiter.engine.extension.ExtensionRegistry;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.commons.util.BlacklistedExceptions;
import org.junit.platform.commons.util.Preconditions;
import org.junit.platform.commons.util.ReflectionUtils;
import org.junit.platform.commons.util.StringUtils;
import org.junit.platform.engine.ConfigurationParameters;
Expand Down Expand Up @@ -85,14 +85,13 @@ public class ClassTestDescriptor extends JupiterTestDescriptor {
private List<Method> afterAllMethods;

public ClassTestDescriptor(UniqueId uniqueId, Class<?> testClass, ConfigurationParameters configurationParameters) {
this(uniqueId, ClassTestDescriptor::generateDefaultDisplayName, testClass, configurationParameters);
this(uniqueId, testClass, () -> getDisplayNameGenerator(testClass).generateDisplayNameForClass(testClass),
configurationParameters);
}

protected ClassTestDescriptor(UniqueId uniqueId, Function<Class<?>, String> defaultDisplayNameGenerator,
Class<?> testClass, ConfigurationParameters configurationParameters) {

super(uniqueId, determineDisplayName(Preconditions.notNull(testClass, "Class must not be null"),
defaultDisplayNameGenerator), ClassSource.from(testClass));
ClassTestDescriptor(UniqueId uniqueId, Class<?> testClass, Supplier<String> displayNameSupplier,
ConfigurationParameters configurationParameters) {
super(uniqueId, testClass, displayNameSupplier, ClassSource.from(testClass));

this.testClass = testClass;
this.tags = getTags(testClass);
Expand Down Expand Up @@ -121,12 +120,6 @@ public String getLegacyReportingName() {
return this.testClass.getName();
}

private static String generateDefaultDisplayName(Class<?> testClass) {
String name = testClass.getName();
int index = name.lastIndexOf('.');
return name.substring(index + 1);
}

// --- Node ----------------------------------------------------------------

@Override
Expand Down
Loading

0 comments on commit 8f18466

Please sign in to comment.