JUnit @DisplayName and Custom Display Name Generator

JUnit5 has the capability to give a custom name to a test method or class. It also provides the functionality to extend the DisplayNameGenerator class and provide a custom strategy to name the tests.

We will go through annotations like @DisplayName, @DisplayNameGeneration, @IndicativeSentencesGeneration etc.


1. @DisplayName

@DisplayName can be used on a test class or a method to provide a custom name.

This annotation can contain spaces, special characters, emojis etc.

@DisplayName("Custom Display Name Test Class")
class DisplayNameTest 
    @Test
    @DisplayName("Custom method name $%^! ? ?")
    void displayNameWithSpecialCharsAndEmoji() {
    }
}
Output
Custom Display Name Test Class [ com.codingeek.DisplayNameTest ]
    ✓  Custom method name $%^! ? ?

2. @DisplayNameGeneration

@DisplayNameGeneration annotation defines the strategy that should be used to generate the test names. This can be used only for classes and not for specific methods.

For start let us have a look at how DisplayNameGenerator.ReplaceUnderscores.class works.

Note@DisplayName always takes precedence over the generated name.

@Nested
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
  class Replace_underscores_generation_test {
    @Test
    void should_generate() {}

    @DisplayName("@DisplayName annotation is preferred")
    @Test
    void this_should_not_generate() {}
  }
Output
Custom Display Name Test Class [ com.codingeek.DisplayNameTest ]
Replace underscores generation test [ com.codingeek.DisplayNameTest$Replace_underscores_generation_test ]
    ✓  should generate
    ✓  @DisplayName annotation is preferred

3. @IndicativeSentencesGeneration

@IndicativeSentencesGeneration annotation generates the names by concatenating the method names and the enclosing test class name.

Let us see an example of how the test name is generated in indicative sentence generation along with @ParameterizedTest annotation.

@Nested
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
  class A_string_is_valid_replace_underscores {

    @Test
    void if_it_is_not_blank() {}

    @DisplayName("@DisplayName - if it has only numbers or alphabets")
    @ParameterizedTest(name = "{displayName} -> {0} is a valid string")
    @ValueSource(strings = {"a", "1", "a1"})
    void if_it_has_only_number_or_alphabets(String value) {}
  }
Output
Custom Display Name Test Class [ com.codingeek.DisplayNameTest ]
DisplayNameTest$A string is not valid indicative generation [ com.codingeek.DisplayNameTest$A_string_is_not_valid_indicative_generation ]
    ✓  @DisplayName - if it has special characters -> @ is not a valid string
    ✓  @DisplayName - if it has special characters -> a$ is not a valid string
    ✓  @DisplayName - if it has special characters -> a@$ is not a valid string
    ✓  DisplayNameTest$A string is not valid indicative generation -> if it is blank

4. Custom Name Generator

Generally in Java tests, we use camelcase naming convention instead of underscore separated one.

So we can write a custom implementation to implement the DisplayNameGenerator interface to define the strategy to generate the name.

In the implementation, we have replaced the camel case method and class names with the space-separated statements. Instead of implementing completely, we have started by extending DisplayNameGenerator.Standard.class

Example- validateStringLengthTest will change to validate string length test

static class CamelCaseToStatementGenerator extends DisplayNameGenerator.Standard {
    @Override
    public String generateDisplayNameForClass(Class<?> testClass) {
      return camelCaseToSpaceSeparatedStatement(super.generateDisplayNameForClass(testClass));
    }

    @Override
    public String generateDisplayNameForNestedClass(Class<?> nestedClass) {
      return camelCaseToSpaceSeparatedStatement(super.generateDisplayNameForNestedClass(nestedClass));
    }

    @Override
    public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
      return this.camelCaseToSpaceSeparatedStatement(testMethod.getName())
          + DisplayNameGenerator.parameterTypesAsString(testMethod);
    }

String camelCaseToSpaceSeparatedStatement(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();
    }
  }

We can now use the CamelCaseToStatementGenerator and rewrite the test classes discussed above in @DisplayNameGeneration and @IndicativeSentenceGeneration.

@DisplayName("Custom Display Name Test Class")
class DisplayNameTest {

  @Nested
  @DisplayNameGeneration(CamelCaseToStatementGenerator.class)
  class AStringIsValidCustomAnnotation {

    @Test
    void ifItIsNotBlank() {}

    @DisplayName("@DisplayName - if it has only numbers or alphabets")
    @ParameterizedTest(name = "{displayName} -> {0} is a valid string")
    @ValueSource(strings = {"a", "1", "a1"})
    void ifItHasOnlyNumberOrAlphabets(String value) {}
  }

  @Nested
  @IndicativeSentencesGeneration(
      separator = " -> ",
      generator = CamelCaseToStatementGenerator.class)
  class AStringIsNotValidCustomAnnotation {

    @Test
    void ifItIsBlank() {}

    @DisplayName("@DisplayName - if it has special characters")
    @ParameterizedTest(name = "{displayName} -> {0} is not a valid string")
    @ValueSource(strings = {"@", "a$", "a@$"})
    void ifItHasOnlyNumberOrAlphabets(String value) {}
  }
}
Output
Custom Display Name Test Class [ com.codingeek.DisplayNameTest ]
Display name test$ a string is not valid custom annotation [ com.codingeek.DisplayNameTest$AStringIsNotValidCustomAnnotation ]
    ✓  @DisplayName - if it has special characters -> @ is not a valid string
    ✓  @DisplayName - if it has special characters -> a$ is not a valid string
    ✓  @DisplayName - if it has special characters -> a@$ is not a valid string
    ✓  Display name test$ a string is not valid custom annotation -> if it is blank()

A string is valid custom annotation [ com.codingeek.DisplayNameTest$AStringIsValidCustomAnnotation ]
    ✓  @DisplayName - if it has only numbers or alphabets -> a is a valid string
    ✓  @DisplayName - if it has only numbers or alphabets -> 1 is a valid string
    ✓  @DisplayName - if it has only numbers or alphabets -> a1 is a valid string
    ✓  if it is not blank()

5. ParameterizedTest and DisplayName

In the case of @ParameterizedTests, the display name is generated based on the value of the field name that we define, so if we do not use {displayName} placeholder then the @DisplayName or the name generation schemes will be ignored.

Let’s have a quick look at the example, this is also used in all the code samples above in this article.

@DisplayName("@DisplayName - if it has special characters")
@ParameterizedTest(name = "{displayName} -> {0} is not a valid string")
@ValueSource(strings = {"@", "a$", "a@$"})
void ifItHasOnlyNumberOrAlphabets(String value) {}

6. Conclusion

In this tutorial, we discussed how we can use @DisplayName to name the classes and methods. Also how we can make use of DisplayNameGenerator API to define the custom name generation strategy.

Complete code samples are present on Github project.


An investment in knowledge always pays the best interest. I hope you like the tutorial. Do come back for more because learning paves way for a better understanding

Do not forget to share and Subscribe.

Happy coding!! ?

Recommended -

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x
Index