«1. Обзор

JUnit 5 имеет хорошую поддержку для настройки имен тестовых классов и тестовых методов. В этом кратком руководстве мы увидим, как мы можем использовать настраиваемые генераторы отображаемых имен JUnit 5 с помощью аннотации @DisplayNameGeneration.

2. Генерация отображаемых имен

Мы можем настроить собственные генераторы отображаемых имен с помощью аннотации @DisplayNameGeneration. Однако полезно знать, что аннотация @DisplayName всегда имеет приоритет над любым генератором отображаемого имени.

Для начала JUnit 5 предоставляет класс DisplayNameGenerator.ReplaceUnderscores, который заменяет любые символы подчеркивания в именах пробелами. Давайте рассмотрим пример:

@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() {
        }
    }
}

Теперь, когда мы запустим тест, мы увидим, что генерация отображаемого имени сделала вывод теста более читабельным:

└─ ReplaceUnderscoresGeneratorUnitTest ✓
   └─ when doing something ✓
      ├─ then something should happen() ✓
      └─ @DisplayName takes precedence over generation ✓

3. Пользовательский генератор отображаемых имен

Чтобы написать собственный генератор отображаемых имен, нам нужно написать класс, реализующий методы интерфейса DisplayNameGenerator. В интерфейсе есть методы для генерации имени класса, вложенного класса и метода.

3.1. Замена верблюжьего регистра

Начнем с простого генератора отображаемых имен, который заменяет имена верблюжьих регистров читаемыми предложениями. Для начала мы можем расширить класс DisplayNameGenerator.Standard:

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

В приведенном выше примере мы видим методы, генерирующие разные части отображаемого имени.

Давайте напишем тест для нашего генератора:

@DisplayNameGeneration(DisplayNameGeneratorUnitTest.ReplaceCamelCase.class)
class DisplayNameGeneratorUnitTest {

    @Test
    void camelCaseName() {
    }
}

Далее, при запуске теста мы видим, что названия верблюжьих падежей были заменены читаемыми предложениями:

└─ Display name generator unit test ✓
   └─ camel case name() ✓

3.2. Ориентировочные предложения

До сих пор мы обсуждали очень простые варианты использования. Однако мы можем пойти более творчески:

    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()) + ".";
        }
    }

Идея здесь состоит в том, чтобы создать показательные предложения из вложенного класса и тестового метода. Другими словами, имя вложенного класса будет добавлено к имени тестового метода:

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) {
        }
    }
}

Глядя на пример, мы используем вложенный класс в качестве контекста для тестового метода. Чтобы лучше проиллюстрировать результаты, давайте запустим тест:

└─ 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. ✓

Как мы видим, генератор объединил имена вложенных классов и тестовых методов для создания показательных предложений.

4. Заключение

В этом руководстве мы увидели, как использовать аннотацию @DisplayNameGeneration для создания отображаемых имен для наших тестов. Кроме того, мы написали собственный DisplayNameGenerator для настройки генерации отображаемого имени.

Как обычно, примеры, использованные в этой статье, можно найти в проекте GitHub.