首页 > 解决方案 > 在多个接口实现上运行相同的参数化测试集

问题描述

我正在用 Java 实现算法系列,并希望在每个算法实现上运行相同的一组算法系列测试。我们来看排序算法案例。

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

我多次看到“如何在多个类实现上运行相同的测试集”的答案是使用参数化测试和要测试的实现类列表作为输入。但是,这些示例使用类作为唯一参数。我想在被测类上运行参数化测试。

我遇到的参数化测试的在线示例使用简单的参数(单个对象/基元或对象/基元列表)。就我而言,我想提供要测试的类一组值。我认为这是可能的,但感觉很难看,我必须像这样对每个类类型重复相同的测试用例(不是实际的 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。

标签: unit-testingjunitjunit5

解决方案


就我而言,我想提供要测试的类一组值。

您可以在例如 a 中组合多个源@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));
    }

}

如果您不想使用提供预期结果的测试预言机(即通过参考实现的排序列表),您还可以组合三个流:sorters(), unsorted(), sorted(). 此外,Supplier如果你想懒惰地创建你的测试类,你可以使用 s :

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

推荐阅读