如何JUnittesting两个List <E>包含相同的顺序相同的元素?

上下文

我正在为MyObject类写一个简单的JUnittesting。

一个MyObject可以从一个静态工厂方法创build,该方法使用String的可变参数。

 MyObject.ofComponents("Uno", "Dos", "Tres"); 

MyObject存在的任何时候,客户端可以通过.getComponents()方法,以List <E>的forms检查它创build的参数。

 myObject.ofComponents(); // -> List<String>: { "Uno", "Dos", "Tres" } 

换句话说,一个MyObject都会记住并显示使其存在的参数列表。 关于这个合同的更多细节:

  • getComponents的顺序将与用于创build对象的顺序相同
  • 重复后续的string组件是允许和保留的顺序
  • null上的行为是未定义的(其他代码保证没有null到工厂)
  • 在对象实例化之后,没有办法改变组件的列表

我正在写一个简单的testing,从String列表创build一个MyObject ,并检查它是否可以通过.getComponents()返回相同的列表。 我立即做了这个,但是这应该是在一个现实的代码path上发生的

这里我的尝试:


 List<String> argumentComponents = Lists.newArrayList("One", "Two", "Three"); List<String> returnedComponents = MyObject.ofComponents( argumentComponents.toArray(new String[argumentComponents.size()])) .getComponents(); assertTrue(Iterables.elementsEqual(argumentComponents, returnedComponents)); 

  • 谷歌Guava Iterables.elementsEqual()最好的方式,只要我在我的构buildpath库,比较这两个列表? 这是我一直在痛苦的事情; 我应该使用这个帮助器方法,通过一个可重用的<E> ..检查大小,然后迭代运行.equals() ..或互联网searchbuild议的任何其他方法? 比较unit testing列表的规范方法是什么?

我希望获得可选的见解

  • 方法testing是否合理devise? 我不是JUnit的专家!
  • .toArray()是将List <E>转换为E的可变参数的最佳方式吗?

我更喜欢使用Hamcrest,因为如果出现故障,它会提供更好的输出

 Assert.assertThat(listUnderTest, IsIterableContainingInOrder.contains(expectedList.toArray())); 

而不是报告

expected true, got false

它会报告

expected List containing "1, 2, 3, ..." got list containing "4, 6, 2, ..."

IsIterableContainingInOrder.contain

Hamcrest

根据Javadoc:

为Iterables创build一个匹配器,匹配的时候,一次遍历被检查的Iterable会生成一系列的项目,每个项目在逻辑上等于指定项目中的相应项目。 对于正匹配,被检查的迭代必须与指定项目的数量相同

因此, listUnderTest必须具有相同数量的元素,每个元素必须按顺序匹配期望的值。

为什么不简单地使用List#equals

 assertEquals(argumentComponents, imapPathComponents); 

List#equals合同List#equals

如果两个列表按照相同的顺序包含相同的元素,则它们被定义为相等的。

所以你的List实现上的equals()方法应该做元素比较

 assertEquals(argumentComponents, returnedComponents); 

是很容易的。

org.junit.Assert.assertEquals()org.junit.Assert.assertArrayEquals()完成这个工作。

为了避免下一个问题:如果你想忽略所有的元素来设置,然后比较: Assert.assertEquals(new HashSet<String>(one), new HashSet<String>(two))

但是,如果你只是想忽略重复,但保留顺序包装与LinkedHashSet列表。

又一个小费。 技巧Assert.assertEquals(new HashSet<String>(one), new HashSet<String>(two))工作正常,直到比较失败。 在这种情况下,它向您显示错误消息,可能会令您感到困惑,因为集合中的顺序几乎不可预测(至less对于复杂对象)。 所以,我发现的技巧是用有序集而不是HashSet来包装集合。 您可以使用TreeSet与自定义比较器。

对于优秀的代码可读性, Fest Assertions对断言列表有很好的支持

所以在这种情况下,就像这样:

 Assertions.assertThat(returnedComponents).containsExactly("One", "Two", "Three"); 

或者把预期的列表做成一个数组,但我更喜欢上面的方法,因为它更清晰。

 Assertions.assertThat(returnedComponents).containsExactly(argumentComponents.toArray()); 
  • 我的答案是否Iterables.elementsEqual是最好的select:

Iterables.elementsEqual足以比较2 List

Iterables.elementsEqual用于更一般的场景,它接受更一般的types: Iterable 。 也就是说,你甚至可以将ListSet进行比较。 (通过迭代次序,这很重要)

当然ArrayListLinkedList定义相当不错,你可以直接调用equals。 而当你使用一个不明确定义的列表时, Iterables.elementsEqual是最好的select。 有一件事应该注意: Iterables.elementsEqual不接受null

  • 将List转换为数组: Iterables.toArray是easer。

  • 对于unit testing,我build议在testing用例中添加空列表