JUnitカスタム表示名ジェネレーターAPI

  • link:/category/testing/ [テスト]

  • JUnit

1. 概要

JUnit 5は、テストクラスとテストメソッド名のカスタマイズを適切にサポートしています。 このクイックチュートリアルでは、_ @ DisplayNameGeneration_アノテーションを使用してJUnit 5のカスタム表示名ジェネレーターを使用する方法を説明します。

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_を作成しました。
いつものように、この記事で使用されている例は、https://github.com/eugenp/tutorials/tree/master/testing-modules/junit-5-advanced [GitHub project]にあります。