web-dev-qa-db-ja.com

JUnit 5で文字列配列を使用してパラメーター化する方法

文字列配列(String[])をパラメーターとして取るJUnit 5パラメータ化テストを記述したいと思います。

@ParameterizedTest
@MethodSource("stringArrayProvider")
void parseFirstAndSecondInt(String[] args) {
    Arguments arguments = new Arguments(args);

    assertEquals(1, arguments.getFirst());
    assertEquals(2, arguments.getSecond());
}

文字列配列のコレクション/ストリーム/イテレータを提供する方法がわかりません。私は@MethodSourceアノテーションを使用したアプローチに失敗しました

static Stream<String[]> stringArrayProvider() {
    return Stream.of(
            new String[]{"1", "2"},
            new String[]{"1", "2", "3"});
}

しかし、私はこの例外を受けています:

org.junit.jupiter.params.converter.ArgumentConversionException:
    No implicit conversion to convert object of type Java.lang.String to type [Ljava.lang.String;

そのような種類のパラメーター化されたテストを行うための良い設計/ソリューションは何でしょうか?

11
Vít Kotačka

_org.junit.jupiter.params.provider.Arguments_のArguments.of()ファクトリを使用して、引数をラップします。

_static Stream<Arguments> stringArrayProvider() {
    return Stream.of(
            Arguments.of((Object) new String[]{"1", "2"}),
            Arguments.of((Object) new String[]{"1", "2", "3"})
    );
}
_

詳細については、 http://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests を参照してください

3
Sormuras

私が使用する最初の一般的な経験則:

  • 同じ生成されたテストケースを複数のテストクラスで使用できる場合は、@ArgumentSource(ソリューション)を使用します

  • 同じ生成されたテストケースを(同じクラスの)複数のテストメソッドで使用できる場合は、@MethodSourceSormuras solution)を使用します。

  • それ以外の場合は、テストケースのソースを、それらを使用するメソッドにできるだけローカルに保つようにします。

この最後の状況では、2つの単純な可能性を想定しています。

  1. あなたは、固定数の文字列に興味があります(したがって、本当に配列は必要ありません)。 @CsvSourceを使用できます

    ここに2つの文字列の例があります(おそらくコンマも含まれています)。

    @ParameterizedTest
    @CsvSource({ "foo, 1", "bar, 2", "'baz, qux', 3" })
    void testWithCsvSource(String first, String second) {
        assertNotNull(first);
        assertNotEquals(0, second);
    }
    

    この場合、さまざまな要素が実際には文字列ではない場合、おそらく自動的に正しい型に解析される可能性があることに注意してください(たとえば、質問では整数が必要なようです)

  2. 本当に可変サイズの文字列配列が必要です。この場合、@ValueSourceを使用して、その内容をString []に変換できます。

    直接:

    @ParameterizedTest
    @ValueSource(strings = {"1, 2",
                            "1, 2, 3"})
    void testWithArrayOfStrings(String arg) {       // the single csv String parameter
      String[] arrayParam = arg.split("\\s*,\\s*"); // is converted to an array of Strings
      ...
    }
    

    または、@ ConvertWithで示される明示的なコンバータークラスを使用します。

    @ParameterizedTest
    @ValueSource(strings={"1, 2", "1, 2, 3"})
    void testWithArrayOfStrings(@ConvertWith(CSVtoArray.class)String... arg) 
    {
      ...
    }
    
    public static class CSVtoArray extends SimpleArgumentConverter {
      @Override
      protected Object convert(Object source, Class<?> targetType) throws ArgumentConversionException {
        String s = (String) source;
        return s.split("\\s*,\\s*");
      }
    }
    
5

Sormurasのソリューション の代替案は、アノテーション@ArgumentsSourceの使用で、非常によく似た方法で機能します。

static class StringArrayProvider implements ArgumentsProvider {
    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {
        return Stream.of(
                (Object) new String[]{"1", "2"},
                (Object) new String[]{"1", "2", "3"}).map(Arguments::of);
    }
}

それでも、String[]からObjectへのキャストは奇妙に見え、Niceデザインよりも回避策を感じています。

0
Vít Kotačka