如何将列表作为 JUnit5 的参数化测试参数传递?



我想使用三个参数参数化我的 JUnit5 测试:stringstringlist<string>

到目前为止,使用@CsvSource时没有运气,这是我的用例传递参数的最方便方式:

没有隐式转换将 java.lang.String 类型的对象转换为 type java.util.List

实际测试是:

@ParameterizedTest()
@CsvSource(
  "2,1"
 )
fun shouldGetDataBit(first: Int, second: String, third: List<String>) {
    ...
}

知道这是否可能吗?我在这里使用 Kotlin,但它应该是无关紧要的。

没有理由像StefanE建议的那样使用黑客

在这一点上,我很确定 Junit5 测试参数不支持除基元类型之外的任何其他内容,而 CsvSource 只有一个允许混合类型。

实际上,JUnit Jupiter支持任何类型的参数。只是@CsvSource仅限于少数原始类型和String.

因此,您应该按如下方式使用@MethodSource,而不是使用@CsvSource

@ParameterizedTest
@MethodSource("generateData")
void shouldGetDataBit(int first, String second, List<String> third) {
    System.out.println(first);
    System.out.println(second);
    System.out.println(third);
}
static Stream<Arguments> generateData() {
    return Stream.of(
        Arguments.of(1, "foo", Arrays.asList("a", "b", "c")),
        Arguments.of(2, "bar", Arrays.asList("x", "y", "z"))
    );
}

提供第三个元素作为逗号分隔的字符串,并在测试中将字符串拆分为列表。

在这一点上,我很确定 Junit5 测试参数不支持除基元类型之外的任何其他内容,而 CsvSource 只有一个允许混合类型。

如果您真的想将分隔列表作为 CSV 的一部分传入(例如,也许它确实使代码看起来更干净),您可以使用自定义ArgumentConverter使用 JUnit 的显式转换功能。

@CsvSource({
        "First,A;B",
        "Second,C",
        ",",
        "'',''"
})
@ParameterizedTest
public void testMethod(String string,
                       @ConvertWith(StringToStringListArgumentConverter.class)
                       List<String> list) {
    // ...
}
@SuppressWarnings("rawtypes")
public static class StringToStringListArgumentConverter
        extends TypedArgumentConverter<String, List> {
    protected StringToStringListArgumentConverter() {
        super(String.class, List.class);
    }
    @Override
    protected List convert(String source)
            throws ArgumentConversionException {
        if (source == null) {
            return null;
        } else if (source.isEmpty()) {
            return List.of();
        } else {
            return List.of(source.split(";"));
        }
    }
}

完全通用的列表转换

如果您计划在每个位置使用不同的元素类型的多个位置使用列表转换(例如 List<String>List<Integer>List<LocalDate> ),您可以创建一个泛型参数转换器,该转换器委托给不同的参数转换器以转换不同的元素类型)。

下面是一个使用 Google Guava TypeToken 类来确定列表元素类型以及内置内部 JUnit DefaultArgumentConverter 来执行元素转换的示例:

@CsvSource({
        "First,1;2",
        "Second,3",
        ",",
        "'',''"
})
@ParameterizedTest
public void testMethod(String string,
                       @ConvertWith(StringToListArgumentConverter.class)
                       List<Integer> list) {
    // ...
}
public static class StringToListArgumentConverter
        implements ArgumentConverter {
    @Override
    public Object convert(Object source, ParameterContext context)
            throws ArgumentConversionException {
        if (source != null && !(source instanceof String)) {
            throw new ArgumentConversionException("Source must be a string");
        }
        if (!List.class.isAssignableFrom(context.getParameter().getType())) {
            throw new ArgumentConversionException("Target must be a list");
        }
        String sourceString = (String) source;
        if (sourceString == null) {
            return null;
        } else if (sourceString.isEmpty()) {
            return List.of();
        }
        @SuppressWarnings("UnstableApiUsage")
        Class<?> elementType =
                TypeToken.of(context.getParameter().getParameterizedType())
                        .resolveType(List.class.getTypeParameters()[0])
                        .getRawType();
        return Arrays.stream(sourceString.split(";"))
                .map(s -> DefaultArgumentConverter.INSTANCE
                        .convert(s, elementType))
                .toList();
    }
}

最新更新