如何对Java 8流进行单元测试?

5

list.stream().forEach(e -> { dbCall.delete(e.Id());});

这段代码会删除列表中的每个元素。

假设列表中有3个元素,如何进行单元测试:

  1. 调用了3次删除函数。
  2. 删除函数的调用顺序是否与列表中元素的顺序相同?

1
由于您正在使用明确无序的forEach,因此测试调用是否按顺序完成没有意义。但是,无论如何,您所要求的不是单元测试。您正在尝试验证实现细节。如果该语句执行正确的操作,并且正确的操作是删除指定的三个条目,那就是您应该验证的,谁在乎以什么顺序调用了哪种方法?测试代码是否删除了三个指定的项目的方法,不取决于您是使用循环,list.forEach还是list.stream().forEach(…) - Holger
@Holger forEach 不是'明确无序的';对于顺序流,它保证是有序的,而对于并行流,则是隐式无序的。此外,删除的顺序也可能很重要,例如,如果列表是按数据库强制执行的项目之间的引用关系排序的。这可能有点牵强,也许不是最好的设计,但仍然是可能的。 - daniu
@Holger 我得出这个结论是因为在这种情况下,流来自于一个List,并且行为与调用List#forEach相同,对于它,“操作按迭代顺序执行(如果指定了迭代顺序)”(https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html#forEach-java.util.function.Consumer-)。Brian的陈述是关于一般情况下的`Stream#forEach`调用,例如也适用于从无序`Collection`创建的流。你是对的,“保证”不是真的 - 但JDK从未改变过现有的行为。 - daniu
@Holger 关于单元测试中顺序无关紧要的说法,那并不是真的。"这三个项目已被正确删除"是一个操作需求,严格意义上的单元测试验证技术行为。此外,通常情况下,调用顺序也是单元测试的范畴:如果这些不是数据库删除而是三个不同的服务调用,我相信您会同意顺序可能是相关的,我们就不会有这个讨论了。 - daniu
@Holger 嗯,这个操作确实不能保证顺序,但是单元测试完全能够检测到,因为它运行在当前的实现上(如果你在测试和生产环境中使用相同的实现)。 - daniu
显示剩余9条评论
2个回答

4
您可以使用JUnit的InOrder
DbCall dbCall = mock(DbCall.class);
List<Element> list = Arrays.asList(newElement(1), newElement(2), newElement(3));

runDeleteMethod(list);

InOrder inorder = inOrder(dbCall);
inorder.verify(dbCall).delete(1);
inorder.verify(dbCall).delete(2);
inorder.verify(dbCall).delete(3);

1

只需验证应该调用的预期时间dbCall.delete()。 可以是这样的:

Mockito.verify(dbCall, times(3L)).delete(any(String.class));

流可以并行工作,因此您无法验证顺序。您可以通过索引上的verify元素来执行此操作,但将忽略顺序。Mockito只会验证是否使用了调用+值。这对于单元测试已经足够了。


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