如何在JUnit 5中使用字符串数组进行参数化

25

我想编写一个JUnit 5参数化测试,它将字符串数组 (String[]) 作为参数:

Translated text:

我想编写一个JUnit 5参数化测试,它将字符串数组 (String[]) 作为参数:

@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;

有什么好的设计/解决方案可以实现这种参数化测试?

3个回答

18

2
这个可以用,谢谢。但是转换成 Object 看起来有点奇怪。 - Vít Kotačka
1
由于工厂方法的“…”可变参数特性,List.of((Object) new String[]{"1", "2"})List.of(new String[]{"1", "2"}) 并不相等,就像使用 List.of() 一样。 - Sormuras
IDEA 注意事项:参数 'new String[] { "1", "2" }' 令人困惑,不清楚是想要可变参数调用还是非可变参数调用。 - Sormuras

10

我使用的第一条通用规则:

  • 当多个测试类可以使用相同的生成测试用例时,请使用 @ArgumentSource (您的解决方案)

  • 当同一类中的多个测试方法可以使用相同的生成测试用例时,请使用 @MethodSource (Sormuras' 解决方案)

  • 否则,请尽可能将测试用例源保持本地化,与使用它们的方法保持在同一方法中。

在最后一种情况下,我设想有两种简单的可能性:

  1. 您对固定数量的字符串感兴趣(因此不需要数组)。您可以使用 @CsvSource

这里有一个包含两个字符串的示例(可能还包括逗号)。

    @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*");
      }
    }

0

可以作为Sormuras' solution的替代方案,使用注解@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 看起来很奇怪,我更倾向于感觉是一种变通而不是良好的设计。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接