以更智能的方式在Junit 5(或其他测试Java库)中使用数组进行参数化

Lor*_*ore 20 java junit unit-testing parameterized-unit-test junit5

我正在尝试参数化这个测试:

@Test
public void reverseQuote(double[] qsp) throws Exception {
...}
Run Code Online (Sandbox Code Playgroud)

对我来说似乎很荒谬,它不存在一些初始化数组的快速方法qsp,例如ValueSource:

@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testWithValueSource(int argument) {
    assertNotNull(argument);
}
Run Code Online (Sandbox Code Playgroud)

我的目标是做一些事情@ValueSource(doublesArray = {new double[]{1.0, 2.0, 3.0}})(现在返回错误).不存在什么允许类似的东西? 其他答案似乎只是建议的方式,如使用@MethodSource@ConvertWith.

我也接受实施其他测试库的答案.

Die*_*ter 8

使用Junit参数化测试和YAML解析的组合可能需要考虑.

@RunWith(Parameterized.class)
public class AnotherParameterizedTest {

    private final HashMap row;

    @Parameterized.Parameters(name="Reverse Lists Tests # {index}:")
    public static List<Map<String, Object>> data() {
        final TestData testData = new TestData(""+
             "|   ID   |       List         |  Expected   |                \n"+
             "|   0    |    [1, 2, 3]       |  [3, 2, 1]  |                \n"+
             "|   1    |    [2, 3, 5]       |  [3, 2, 1]  |                \n"+
             "|   2    |    [5, 6, 7]       |  [ 7, 6, 5] |                \n"
        );
        // parsing each row using simple YAML parser and create map per row
        return testData.getDataTable();
    }

    // Each row from the stringified table above will be 
    // split into key=value pairs where the value are parsed using a 
    // yaml parser. this way, values can be pretty much any yaml type
    // like a list of integers in this case. 
    public AnotherParameterizedTest(HashMap obj) {
        this.row = obj;
    }

    @Test
    public void test() throws Exception {
        List orgListReversed = new ArrayList((List) row.get("List"));
        Collections.reverse(orgListReversed);
        assertEquals((List) row.get("Expected"), orgListReversed);
    }

}
Run Code Online (Sandbox Code Playgroud)

我没有使用字符串,而是使用Excel Reader对简单的Excel表格执行相同的操作.使用YAML将每个行解析为一个Map.

Junit IDE测试结果

刚刚使用Junit Jupiter测试的结果在IDE Runner中提供了更好的结果.

import static org.junit.jupiter.api.Assertions.assertEquals;

import de.deicon.yatf.runner.dsl.TestData;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.Collections;
import java.util.List;
import java.util.Map;

public class FirstTest {

    @ParameterizedTest
    @MethodSource("testTable")
    public void test(Map row){
        List reversedList = (List) row.get("List");
        Collections.reverse(reversedList);
        assertEquals((List)row.get("Expected"), reversedList);
    }

    static List<Map<String, Object>> testTable() {
        return new TestData(""+
                "|ID|   List                  |Expected               |         \n"+
                "|0 | [1,2,3]                 | [3,2,1]               |         \n"+
                "|1 | [hans, peter, klaus]    | [klaus, peter, hans]  |         \n"
        ).getDataTable();
    }

}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


Sha*_*dov 7

好吧,这将是一个奇怪的答案,但它的工作原理,这样做很有趣.

第一件事:你的方式是不可能的.不是因为JUnit或任何相关的API,而是因为Java - 有效的注释类型元素(注释参数只能是原始的,String,Class,Enum,其他注释和所有那些的数组).

第二件事:我们可以绕过第一个.检查一下:

@ArraySources(
  arrays = {
    @ArraySource(array = {1, 2, 3}),
    @ArraySource(array = {4, 5, 6}),
    @ArraySource(array = {7, 8, 9})
  }
)
Run Code Online (Sandbox Code Playgroud)

正如它所说,注释可以有其他注释作为参数,以及那些注释的数组,所以我们在这里使用这两个规则.

第三件事:这有什么帮助?我们可以添加自己的注释+参数提供者,JUnit 5就是这样可扩展的.

两个注释:

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(ArrayArgumentsProvider.class)
public @interface ArraySources {
    ArraySource[] arrays();
}

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ArraySource {
    int[] array() default {};
}
Run Code Online (Sandbox Code Playgroud)

基于注释的参数提供程序:

public class ArrayArgumentsProvider implements ArgumentsProvider, AnnotationConsumer<ArraySources> {
    private List<int[]> arguments;

    public void accept(ArraySources source) {
        List<ArraySource> arrays = Arrays.asList(source.arrays());

        this.arguments = arrays.stream().map(ArraySource::array).collect(Collectors.toList());
    }

    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
        return this.arguments.stream().map(Arguments::of);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后的测试使用那些:

public class ArraySourcesTest {
    @ParameterizedTest
    @ArraySources(
            arrays = {
                    @ArraySource(array = {1, 2, 3}),
                    @ArraySource(array = {4, 5, 6}),
                    @ArraySource(array = {7, 8, 9})
            }
    )
    void example(int[] array) {
        System.out.println(Arrays.toString(array));
        System.out.println("Test Over");
    }
}

/* Output
[1, 2, 3]
Test Over
[4, 5, 6]
Test Over
[7, 8, 9]
Test Over
*/
Run Code Online (Sandbox Code Playgroud)

你提到的@MethodSource很复杂,所以我认为我在这件事上失败了,但它确实有效.它可以明显地简化和增强(比如将注释参数命名为默认值 - 值 - 我只是int为了显示这个想法而做的).不确定您是否可以使用现有功能(ArgumentsProviderArgumentSources)实现相同功能,但这看起来更具体(您知道您正在使用数组)并显示扩展JUnit5的可能性,在其他情况下可能会有用.


use*_*601 5

我喜欢使用Spock来测试Java代码。这是一个基于groovy的测试框架,位于JUnit 4之上。Spock中的参数化测试是一个内置功能:

def "The reverseQuote method doesn't return null"(double[] qsp) {

    when: "reverseQuote is called"
    double[] rev = reverseQuote(qsp)

    then: "the result is not null"
    null != rev

    where: "there are various input values"
    qsp << [
        [0.1, 0.2, 0.3] as double[],
        [1.0, 2.0, 3.0] as double[]
    ]
}
Run Code Online (Sandbox Code Playgroud)

...或者,您可以以表格形式布置测试数据:

def "The reverseQuote method reverses the input array"(List qsp, List expected) {

    when: "reverseQuote is called"
    double[] rev = reverseQuote(qsp as double[])

    then: "the result is the reverse of the input"
    expected as double[] == rev

    where: "there are various input values"
    qsp             | expected
    [0.1, 0.2, 0.3] | [0.3, 0.2, 0.1]
    [1.0, 2.0, 3.0] | [3.0, 2.0, 1.0]
}
Run Code Online (Sandbox Code Playgroud)

请注意,这种as double[]普遍性是Groovy自动将数组转换为List的不幸结果,因此,在与实际需要数组的Java代码进行交互的特殊情况下,我们必须显式地将其回退。