1. Overview
JUnit 5 has good support for customizing test class and test method names. In this quick tutorial, we’ll see how we can use JUnit 5 custom display name generators via the @DisplayNameGeneration annotation.
2. Display Name Generation
We can configure custom display name generators via the @DisplayNameGeneration annotation. However, it’s good to be aware that the @DisplayName annotation always takes precedence over any display name generator.
To start with, JUnit 5 provides a DisplayNameGenerator.ReplaceUnderscores class that replaces any underscores in names with spaces. Let’s take a look at an example:
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) class ReplaceUnderscoresGeneratorUnitTest { @Nested class when_doing_something { @Test void then_something_should_happen() { } @Test @DisplayName("@DisplayName takes precedence over generation") void override_generator() { } } }
Now, when we run the test we can see the display name generation made the test output more readable:
└─ ReplaceUnderscoresGeneratorUnitTest ✓ └─ when doing something ✓ ├─ then something should happen() ✓ └─ @DisplayName takes precedence over generation ✓
3. Custom Display Name Generator
In order to write a custom display name generator, we have to write a class that implements the methods in the DisplayNameGenerator interface. The interface has methods for generating the name for a class, a nested class, and a method.
3.1. Camel Case Replacement
Let’s start with a simple display name generator that replaces camel case names with readable sentences. To begin with, we can extend the DisplayNameGenerator.Standard class:
static class ReplaceCamelCase extends DisplayNameGenerator.Standard { @Override public String generateDisplayNameForClass(Class<?> testClass) { return replaceCamelCase(super.generateDisplayNameForClass(testClass)); } @Override public String generateDisplayNameForNestedClass(Class<?> nestedClass) { return replaceCamelCase(super.generateDisplayNameForNestedClass(nestedClass)); } @Override public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) { return this.replaceCamelCase(testMethod.getName()) + DisplayNameGenerator.parameterTypesAsString(testMethod); } String replaceCamelCase(String camelCase) { StringBuilder result = new StringBuilder(); result.append(camelCase.charAt(0)); for (int i=1; i<camelCase.length(); i++) { if (Character.isUpperCase(camelCase.charAt(i))) { result.append(' '); result.append(Character.toLowerCase(camelCase.charAt(i))); } else { result.append(camelCase.charAt(i)); } } return result.toString(); } }
In the above example, we can see the methods that generate different parts of the display name.
Let’s write a test for our generator:
@DisplayNameGeneration(DisplayNameGeneratorUnitTest.ReplaceCamelCase.class) class DisplayNameGeneratorUnitTest { @Test void camelCaseName() { } }
Next, when running the test, we can see that the camel case names have been replaced with readable sentences:
└─ Display name generator unit test ✓ └─ camel case name() ✓
3.2. Indicative Sentences
So far, we’ve discussed very simple use cases. However, we can get more creative:
static class IndicativeSentences extends ReplaceCamelCase { @Override public String generateDisplayNameForNestedClass(Class<?> nestedClass) { return super.generateDisplayNameForNestedClass(nestedClass) + "..."; } @Override public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) { return replaceCamelCase(testClass.getSimpleName() + " " + testMethod.getName()) + "."; } }
The idea here is to create indicative sentences from the nested class and test method. In other words, the nested class name will be prepended to the test method name:
class DisplayNameGeneratorUnitTest { @Nested @DisplayNameGeneration(DisplayNameGeneratorUnitTest.IndicativeSentences.class) class ANumberIsFizz { @Test void ifItIsDivisibleByThree() { } @ParameterizedTest(name = "Number {0} is fizz.") @ValueSource(ints = { 3, 12, 18 }) void ifItIsOneOfTheFollowingNumbers(int number) { } } @Nested @DisplayNameGeneration(DisplayNameGeneratorUnitTest.IndicativeSentences.class) class ANumberIsBuzz { @Test void ifItIsDivisibleByFive() { } @ParameterizedTest(name = "Number {0} is buzz.") @ValueSource(ints = { 5, 10, 20 }) void ifItIsOneOfTheFollowingNumbers(int number) { } } }
Looking at the example, we use the nested class as a context for the test method. To better illustrate the results, let’s run the test:
└─ Display name generator unit test ✓ ├─ A number is buzz... ✓ │ ├─ A number is buzz if it is one of the following numbers. ✓ │ │ ├─ Number 5 is buzz. ✓ │ │ ├─ Number 10 is buzz. ✓ │ │ └─ Number 20 is buzz. ✓ │ └─ A number is buzz if it is divisible by five. ✓ └─ A number is fizz... ✓ ├─ A number is fizz if it is one of the following numbers. ✓ │ ├─ Number 3 is fizz. ✓ │ ├─ Number 12 is fizz. ✓ │ └─ Number 18 is fizz. ✓ └─ A number is fizz if it is divisible by three. ✓
As we can see, the generator combined the nested class and test method names to create indicative sentences.
4. Conclusion
In this tutorial, we saw how to use the @DisplayNameGeneration annotation to generate display names for our tests. Furthermore, we wrote our own DisplayNameGenerator to customize the display name generation.
As usual, the examples used in this article can be found in the GitHub project.