我正在为该类编写一个简单的JUnit测试MyObject.
阿MyObject可以从静态工厂方法,该方法的可变参数来创建字符串.
MyObject.ofComponents("Uno", "Dos", "Tres");
Run Code Online (Sandbox Code Playgroud)
在存在期间的任何时候MyObject,客户端都可以通过该方法以List <E>的形式检查它所创建的参数.getComponents().
myObject.ofComponents(); // -> List<String>: { "Uno", "Dos", "Tres" }
Run Code Online (Sandbox Code Playgroud)
换句话说,MyObject两者都会记住并公开使其存在的参数列表.关于这份合同的更多细节:
getComponents将与为对象创建选择的顺序相同null是未定义的(其他代码保证没有null到达工厂)我正在编写一个简单的测试,它MyObject从String列表中创建一个并检查它是否可以返回相同的列表.getComponents().我立即这样做,但这应该发生在一个真实的代码路径中的距离.
在这里我的尝试:
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));
Run Code Online (Sandbox Code Playgroud)
Iterables.elementsEqual()最好的办法,只要我有库在我的构建路径,那些两个列表比较?这是我一直在苦恼的事情; 我应该使用这个辅助方法,它超过了Iterable <E> ..检查大小,然后迭代运行.equals()..或互联网搜索建议的任何其他方法?比较单元测试列表的规范方法是什么?ass*_*ias 71
为什么不简单地使用List#equals?
assertEquals(argumentComponents, imapPathComponents);
Run Code Online (Sandbox Code Playgroud)
如果两个列表包含相同顺序的相同元素,则它们被定义为相等.
Joh*_*n B 54
我更喜欢使用Hamcrest,因为它可以在出现故障时提供更好的输出
Assert.assertThat(listUnderTest,
IsIterableContainingInOrder.contains(expectedList.toArray()));
Run Code Online (Sandbox Code Playgroud)
而不是报告
expected true, got false
它会报告
expected List containing "1, 2, 3, ..." got list containing "4, 6, 2, ..."
IsIterableContainingInOrder.contain
根据Javadoc:
为Iterables创建匹配器,当对检查的Iterable进行单次传递时会生成一系列项目,每个项目在逻辑上等于指定项目中的相应项目.对于肯定匹配,检查的iterable必须与指定项的数量具有相同的长度
因此listUnderTest必须具有相同数量的元素,并且每个元素必须按顺序匹配预期值.
小智 10
List实现上的equals()方法应该进行元素比较,所以
assertEquals(argumentComponents, returnedComponents);
Run Code Online (Sandbox Code Playgroud)
更容易.
Ale*_*exR 10
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))很好,直到比较失败.在这种情况下,它会向您显示错误消息,其中包含您的集合的字符串表示,这可能会令人困惑,因为集合中的顺序几乎不可预测(至少对于复杂对象而言).所以,我发现的技巧是用排序集包装集合而不是HashSet.您可以使用TreeSet自定义比较器.
assertTrue()/assertFalse() :仅用于断言返回的布尔结果
assertTrue(Iterables.elementsEqual(argumentComponents, returnedComponents));
您想使用Assert.assertTrue()orAssert.assertFalse()作为被测方法的返回boolean值。
由于该方法返回一个特定的事物,例如 a ,它应该包含一些预期的元素,因此以这种方式List断言:
是一种反模式。
它使断言易于编写,但当测试失败时,它也使调试变得困难,因为测试运行程序只会对您说类似以下的话:assertTrue()Assert.assertTrue(myActualList.containsAll(myExpectedList)
预期
true但实际是false
Assert.assertEquals(Object, Object)在 JUnit4 或Assertions.assertIterableEquals(Iterable, Iterable)JUnit 5 中:仅用作两者equals(),并toString()为比较对象的类(和深度)覆盖
这很重要,因为断言中的相等测试依赖于比较对象equals(),而测试失败消息则依赖于toString()比较对象。
由于String覆盖了equals()and toString(),因此断言List<String>with 是完全有效的assertEquals(Object,Object)。关于这个问题:您必须equals()在类中进行重写,因为它在对象相等性方面有意义,不仅是为了在 JUnit 测试中使断言更容易。
为了使断言更容易,您还有其他方法(您可以在答案的下一点中看到)。
Guava 是一种执行/构建单元测试断言的方法吗?
如果我的构建路径中有该库,Google Guava Iterables.elementsEqual() 是比较这两个列表的最佳方法吗?
不它不是。Guava 不是一个编写单元测试断言的库。
您不需要它来编写大多数(我认为的)单元测试。
比较单元测试列表的规范方法是什么?
作为一个好的实践,我喜欢断言/匹配器库。
我不能鼓励 JUnit 执行特定的断言,因为这提供的功能确实太少且有限:它仅执行具有深度 equals 的断言。
有时您希望允许元素中的任何顺序,有时您希望允许预期的任何元素与实际匹配,等等......
因此,使用单元测试断言/匹配器库(例如 Hamcrest 或 AssertJ)是正确的方法。
实际答案提供了 Hamcrest 解决方案。这是一个AssertJ解决方案。
org.assertj.core.api.ListAssert.containsExactly()是您所需要的:它验证实际组是否完全包含给定值,而没有其他内容,按所述顺序:
验证实际组是否按顺序准确包含给定值且不包含其他值。
您的测试可能如下所示:
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
@Test
void ofComponent_AssertJ() throws Exception {
MyObject myObject = MyObject.ofComponents("One", "Two", "Three");
Assertions.assertThat(myObject.getComponents())
.containsExactly("One", "Two", "Three");
}
Run Code Online (Sandbox Code Playgroud)
AssertJ 的一个优点是List不需要按预期声明 a :它使断言更直接并且代码更具可读性:
Assertions.assertThat(myObject.getComponents())
.containsExactly("One", "Two", "Three");
Run Code Online (Sandbox Code Playgroud)
如果测试失败:
// Fail : Three was not expected
Assertions.assertThat(myObject.getComponents())
.containsExactly("One", "Two");
Run Code Online (Sandbox Code Playgroud)
您会收到非常明确的消息,例如:
java.lang.断言错误:
期待:
<[“一”、“二”、“三”]>
准确包含(并以相同的顺序):
<[“一”,“二”]>
但有些内容是出乎意料的:
<[“三”]>
断言/匹配器库是必须的,因为它们确实会进一步
假设MyObject不存储Strings 而是Foos 实例,例如:
public class MyFooObject {
private List<Foo> values;
@SafeVarargs
public static MyFooObject ofComponents(Foo... values) {
// ...
}
public List<Foo> getComponents(){
return new ArrayList<>(values);
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个非常普遍的需求。使用 AssertJ,断言的编写仍然很简单。更好的是,您可以断言列表内容是相等的,即使元素的类没有覆盖,equals()/hashCode()而 JUnit 方式要求:
import org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple.tuple;
import org.junit.jupiter.api.Test;
@Test
void ofComponent() throws Exception {
MyFooObject myObject = MyFooObject.ofComponents(new Foo(1, "One"), new Foo(2, "Two"), new Foo(3, "Three"));
Assertions.assertThat(myObject.getComponents())
.extracting(Foo::getId, Foo::getName)
.containsExactly(tuple(1, "One"),
tuple(2, "Two"),
tuple(3, "Three"));
}
Run Code Online (Sandbox Code Playgroud)
为了获得出色的代码可读性,Fest Assertions对断言列表提供了很好的支持
所以在这种情况下,例如:
Assertions.assertThat(returnedComponents).containsExactly("One", "Two", "Three");
Run Code Online (Sandbox Code Playgroud)
或者将预期列表设置为数组,但我更喜欢上述方法,因为它更清晰.
Assertions.assertThat(returnedComponents).containsExactly(argumentComponents.toArray());
Run Code Online (Sandbox Code Playgroud)