编写JUnit测试可能是一个乏味而乏味的过程。 了解如何使用排列结合TestFactory
方法和DynamicTest
对象以最少的编码工作来改进测试类。
在本文中,我将使用Java流ORM Speedment,因为它包含一个现成的Permutation
类,从而帮助我节省了开发时间。 否则,加速可以将数据库表连接到标准Java流。 Speedment是一个开放源代码工具,也有免费版本供商业数据库使用。
测试流
考虑以下JUnit5测试:
@Test
void test() {List<String> actual = Stream.of("CCC", "A", "BB", "BB").filter(string -> string.length() > 1).sorted().distinct().collect(toList());List<String> expected = Arrays.asList("BB", "CCC");assertEquals(actual, expected);
}
如可以看到的,该测试将创建一个Stream
与所述元素“CCC”,“A”,“B-B”和‘BB’,然后应用一个过滤器,将删除‘A’元素(因为它的长度是不大于1的)。 之后,对元素进行排序,以便流中具有元素“ BB”,“ BB”和“ CCC”。 然后,应用独特的操作,删除流中的所有重复项,在调用最终终止运算符之前保留元素“ BB”和“ CCC”,从而将这些其余元素收集到
List
。
经过一番考虑,可以理解,中间操作filter()
, sorted()
和distinct()
的应用sorted()
无关紧要。 因此,无论操作员应用程序的顺序如何,我们都期望得到相同的结果。
但是,我们如何才能找到一个JUnit5测试来证明该顺序对于所有排列都是无关紧要的,而无需手动为所有六个排列编写单独的测试用例?
使用TestFactory
除了编写单个测试,我们还可以使用TestFactory
生成任意数量的DynamicTest
对象。 这是演示该概念的简短示例:
@TestFactory
Stream<DynamicTest> testDynamicTestStream() {return Stream.of(DynamicTest.dynamicTest("A", () -> assertEquals("A", "A")),DynamicTest.dynamicTest("B", () -> assertEquals("B", "B")));
}
这将产生两个可能毫无意义的名为“ A”和“ B”的测试。 请注意,我们如何方便地返回DynamicTest
对象Stream
,而无需先将它们收集到
诸如List
Collection
。
使用排列
Permutation类可用于创建任何类型T
所有可能组合。 这是带有类型的简单示例
String
:
Permutation.of("A", "B", "C").map(is -> is.collect(toList())).forEach(System.out::println);
因为Permutation
创建了类型为T
的Stream
的Stream
,所以我们添加了一个中间映射操作,将内部Stream
收集到List
。 上面的代码将产生以下输出:
[A, B, C]
[A, C, B]
[B, A, C]
[B, C, A]
[C, A, B]
[C, B, A]
容易证明,这是将“ A”,“ B”和“ C”组合在一起的所有方式,每个要素应恰好发生一次。
创建运算符
在本文中,我选择为中间操作创建Java对象,而不是使用lambda,因为我想覆盖toString()
方法并将其用于方法标识。 在其他情况下,直接使用lambda或方法引用就足够了:
UnaryOperator<Stream<String>> FILTER_OP = new UnaryOperator<Stream<String>>() {@Overridepublic Stream<String> apply(Stream<String> s) {return s.filter(string -> string.length() > 1);}@Overridepublic String toString() {return "filter";}};UnaryOperator<Stream<String>> DISTINCT_OP = new UnaryOperator<Stream<String>>() {@Overridepublic Stream<String> apply(Stream<String> s) {return s.distinct();}@Overridepublic String toString() {return "distinct";}
};UnaryOperator<Stream<String>> SORTED_OP = new UnaryOperator<Stream<String>>() {@Overridepublic Stream<String> apply(Stream<String> s) {return s.sorted();}@Overridepublic String toString() {return "sorted";}
};
测试排列
现在,我们可以轻松地在运算符上测试置换的工作方式:
void printAllPermutations() {Permutation.of(FILTER_OP,DISTINCT_OP,SORTED_OP).map(is -> is.collect(toList())).forEach(System.out::println);
}
这将产生以下输出:
[filter, distinct, sorted]
[filter, sorted, distinct]
[distinct, filter, sorted]
[distinct, sorted, filter]
[sorted, filter, distinct]
[sorted, distinct, filter]
可以看出,这些都是我们要测试的中间操作的置换。
拼接起来
通过结合以上学习,我们可以创建TestFactory
,以测试应用于初始流的中间操作的所有排列:
@TestFactory
Stream<DynamicTest> testAllPermutations() {List<String> expected = Arrays.asList("BB", "CCC");return Permutation.of(FILTER_OP,DISTINCT_OP,SORTED_OP).map(is -> is.collect(toList())).map(l -> DynamicTest.dynamicTest(l.toString(),() -> {List<String> actual = l.stream().reduce(Stream.of("CCC", "A", "BB", "BB"),(s, oper) -> oper.apply(s),(a, b) -> a).collect(toList());assertEquals(expected, actual);}));
}
请注意我们如何使用Stream::reduce
方法将中间操作逐步应用于初始Stream.of("CCC", "A", "BB", "BB")
。 合路器lambda
(a, b) -> a
只是一个虚拟对象,仅用于合并并行流(此处未使用)。
爆炸警告
最后,对置换的内在数学复杂性提出了警告。 根据定义,排列的复杂度为O(n!)
,例如,仅将一个元素添加到现有的八个元素的排列中,排列的数量将从40,320增加到362,880。
这是一把双刃剑。 我们几乎免费地获得了许多测试,但是我们必须付出在每个构建上执行每个测试的代价。
码
测试的源代码可以在这里找到。
可以在这里下载Speedment ORM
结论
Permutation
, DynamicTest
和TestFactory
类是用于创建编程JUnit5测试的出色构建块。
注意不要在排列中使用太多元素。 “炸毁”可能意味着两件事……
翻译自: https://www.javacodegeeks.com/2018/10/blow-junit5-tests-permutations.html