在多个接口实现上运行同一组参数化测试

时间:2019-08-14 20:51:45

标签: unit-testing junit junit5

我正在用Java实现算法系列,并希望在每个算法实现上运行相同的算法系列测试集。让我们看一下排序算法的情况。

我有一些排序算法的实现。我想在每个实现上运行相同的参数化测试套件,而不必为每个接口实现复制/粘贴测试。

我已经多次看到“如何在多个类实现上运行相同的测试集”的答案是使用参数化测试,其中包含要测试的实现类列表。但是,这些示例将类用作唯一参数。我想在被测类上运行 parameterized 测试。

我在网上遇到的参数化测试示例使用简单的参数(单个对象/基元或对象/基元列表)。就我而言,我想提供要测试的类值数组。我认为这是可能的,但是感觉很丑,我将不得不对每个类类型重复相同的测试用例,而不是像这样(不是实际的Java语法):

BubbleSorter.class, [1,2,3]
BubbleSorter.class, [3,2,1]
BubbleSorter.class, [-1,2,0]
MergeSorter.class, [1,2,3]
MergeSorter.class, [3,2,1]
MergeSorter.class, [-1,2,0]
InsertionSorter.class, [1,2,3]
...

理想情况下,有一种方法可以设置一次排序实现,并让所有参数化测试仅担心要排序的值列表,而不担心要使用的排序类。

我的直觉是说要使用包含参数化测试的父抽象SorterTest,并使用工厂方法来允许每个子类(例如:MergeSorterTest)决定使用哪种排序器实现。但是,对此的快速搜索似乎暗示了使用继承来重用测试代码中的测试用例的想法。

在这种情况下推荐的方法是什么?在某些情况下,是否允许在测试代码中进行继承,还是总有更好的选择?

我也在使用JUnit 5。

1 个答案:

答案 0 :(得分:0)

  

在我的情况下,我想提供要测试的类值数组。

您可以合并多个来源,例如@MethodSource。假设您有类似通用的Sorter界面:

class SorterTest {

    @ParameterizedTest
    @MethodSource("args")
    void test(Sorter sorter, List<Integer> list) {
        // ...
    }

    static Stream<Arguments> args() {
        // Combines each sorter implementation with each list to be sorted.
        return sorters().flatMap(sorter -> lists().map(list -> Arguments.of(sorter, list)));
    }

    static Stream<Sorter> sorters() {
        return Stream.of(new BubbleSorter(), new MergeSorter(), new InsertionSorter());
    }

    static Stream<List<Integer>> lists() {
        return Stream.of(List.of(1, 2, 3), List.of(3, 2, 1), List.of(-1, 2, 0));
    }

}

如果您不想使用提供预期结果的test oracle(即通过参考实现的排序列表),则还可以合并三个流:sorters(),{{1} },unsorted()。此外,如果要懒惰地创建要测试的类,则可以使用sorted() s:

Supplier