如何在 JUnit 测试用例中的列表之间做出相等断言?列表的内容之间应该是平等的。
例如:
List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");
List<String> numbers3 = Arrays.asList("one", "two", "four");
// numbers should be equal to numbers2
//numbers should not be equal to numbers3
assertArrayEquals
。与 List#toArray
结合使用。
assertArrayEquals
要求您从列表中获取数组。您可以使用 assertIterableEquals
直接对列表进行操作
assertIterableEquals
可用于 jUnit5 @ThetaSinner
对于junit4!这个问题值得为 junit5 写一个新的答案。
我意识到这个答案是在问题发生几年后写的,可能当时还没有这个功能。但是现在,这样做很容易:
@Test
public void test_array_pass()
{
List<String> actual = Arrays.asList("fee", "fi", "foe");
List<String> expected = Arrays.asList("fee", "fi", "foe");
assertThat(actual, is(expected));
assertThat(actual, is(not(expected)));
}
如果您安装了带有 hamcrest 的最新版本的 Junit,只需添加以下导入:
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
http://junit.org/junit4/javadoc/latest/org/junit/Assert.html#assertThat(T, org.hamcrest.Matcher)
http://junit.org/junit4/javadoc/latest/org/hamcrest/CoreMatchers.html
http://junit.org/junit4/javadoc/latest/org/hamcrest/core/Is.html
不要转换为字符串并进行比较。这对性能不利。在junit中,在Corematchers中,有一个匹配器=> hasItems
List<Integer> yourList = Arrays.asList(1,2,3,4)
assertThat(yourList, CoreMatchers.hasItems(1,2,3,4,5));
这是我所知道的检查列表中元素的更好方法。
Assert.assertEquals(4,yourList.size());
assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
toString()
版本不是最好的方法。
对于 JUnit 5
您可以使用 assertIterableEquals
:
List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");
Assertions.assertIterableEquals(numbers, numbers2);
或 assertArrayEquals
并将列表转换为数组:
List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");
Assertions.assertArrayEquals(numbers.toArray(), numbers2.toArray());
testImplementation("junit:junit:4.13")
收件人:testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0")
其他答案中提出的来自 JUnit4/JUnit 5 的 assertEquals(Object, Object)
或来自 Hamcrest 的 assertThat(actual, is(expected));
只有在 equals()
和 toString()
都被比较对象的类(和深度)覆盖时才有效。
这很重要,因为断言中的相等测试依赖于 equals()
,而测试失败消息依赖于比较对象的 toString()
。
对于 String
、Integer
等内置类,对于 . .. 没问题,因为它们同时覆盖了 equals()
和 toString()
。因此,用 assertEquals(Object,Object)
断言 List<String>
或 List<Integer>
是完全有效的。
关于这个问题:您必须在类中覆盖 equals()
,因为它在对象相等性方面是有意义的,而不仅仅是在使用 JUnit 的测试中使断言更容易。
要使断言更容易,您还有其他方法。
作为一种好习惯,我更喜欢断言/匹配器库。
这是一个 AssertJ 解决方案。
org.assertj.core.api.ListAssert.containsExactly()
是您需要的:它验证实际组是否完全包含给定的值,而没有其他内容,按照 javadoc 中所述的顺序。
假设有一个 Foo
类,您可以在其中添加元素以及在哪里获取该元素。
Foo
的单元测试断言两个列表具有相同的内容,可能如下所示:
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
@Test
void add() throws Exception {
Foo foo = new Foo();
foo.add("One", "Two", "Three");
Assertions.assertThat(foo.getElements())
.containsExactly("One", "Two", "Three");
}
AssertJ 的一个优点是不需要按预期声明 List
:它使断言更直接,代码更具可读性:
Assertions.assertThat(foo.getElements())
.containsExactly("One", "Two", "Three");
但是断言/匹配器库是必须的,因为它们真的会更进一步。
现在假设 Foo
不存储 String
而是 Bar
的实例。
这是一个非常普遍的需求。使用 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 add() throws Exception {
Foo foo = new Foo();
foo.add(new Bar(1, "One"), new Bar(2, "Two"), new Bar(3, "Three"));
Assertions.assertThat(foo.getElements())
.extracting(Bar::getId, Bar::getName)
.containsExactly(tuple(1, "One"),
tuple(2, "Two"),
tuple(3, "Three"));
}
这是一个遗留答案,适用于 JUnit 4.3 及更低版本。现代版本的 JUnit 在 assertThat 方法中包含一个内置的可读失败消息。如果可能的话,更喜欢这个问题的其他答案。
List<E> a = resultFromTest();
List<E> expected = Arrays.asList(new E(), new E(), ...);
assertTrue("Expected 'a' and 'expected' to be equal."+
"\n 'a' = "+a+
"\n 'expected' = "+expected,
expected.equals(a));
作为记录,正如@Paul 在他对此答案的评论中提到的那样,两个 List
是相等的:
当且仅当指定的对象也是一个列表时,两个列表的大小相同,并且两个列表中所有对应的元素对都相等。 (如果 (e1==null ? e2==null : e1.equals(e2)) 两个元素 e1 和 e2 相等。)换句话说,如果两个列表以相同的顺序包含相同的元素,则它们被定义为相等.此定义确保 equals 方法在 List 接口的不同实现中正常工作。
请参阅 JavaDocs of the List
interface。
如果您不关心元素的顺序,我建议在 junit-addons 中使用 ListAssert.assertEquals
。
链接:http://junit-addons.sourceforge.net/
对于懒惰的 Maven 用户:
<dependency>
<groupId>junit-addons</groupId>
<artifactId>junit-addons</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
您可以在 junit 中使用 assertEquals。
import org.junit.Assert;
import org.junit.Test;
@Test
public void test_array_pass()
{
List<String> actual = Arrays.asList("fee", "fi", "foe");
List<String> expected = Arrays.asList("fee", "fi", "foe");
Assert.assertEquals(actual,expected);
}
如果元素的顺序不同,则会返回错误。
如果您要断言模型对象列表,那么您应该覆盖特定模型中的 equals 方法。 @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj != null && obj instanceof ModelName) { ModelName other = (ModelName) obj;返回 this.getItem().equals(other.getItem()) ; } 返回假; }
如果你不想建立一个数组列表,你也可以试试这个
@Test
public void test_array_pass()
{
List<String> list = Arrays.asList("fee", "fi", "foe");
Strint listToString = list.toString();
Assert.assertTrue(listToString.contains("[fee, fi, foe]")); // passes
}
List<Integer> figureTypes = new ArrayList<Integer>(
Arrays.asList(
1,
2
));
List<Integer> figureTypes2 = new ArrayList<Integer>(
Arrays.asList(
1,
2));
assertTrue(figureTypes .equals(figureTypes2 ));
我知道已经有很多选项可以解决这个问题,但我宁愿执行以下操作来断言任何 oder 中的两个列表:
assertTrue(result.containsAll(expected) && expected.containsAll(result))
您提到您对列表内容的相等性感兴趣(并且没有提及顺序)。因此,来自 AssertJ 的 containsExactlyInAnyOrder
非常适合。例如,它与 spring-boot-starter-test
一起打包。
从 AssertJ docs ListAssert#containsExactlyInAnyOrder:
验证实际组是否以任何顺序完全包含给定的值,而没有其他任何内容。例子:
// an Iterable is used in the example but it would also work with an array
Iterable<Ring> elvesRings = newArrayList(vilya, nenya, narya, vilya);
// assertion will pass
assertThat(elvesRings).containsExactlyInAnyOrder(vilya, vilya, nenya, narya);
// assertion will fail as vilya is contained twice in elvesRings.
assertThat(elvesRings).containsExactlyInAnyOrder(nenya, vilya, narya);
assertEquals(expected, result);
适合我。由于这个函数有两个对象,你可以向它传递任何东西。
public static void assertEquals(Object expected, Object actual) {
AssertEquals.assertEquals(expected, actual);
}
我不认为以上所有答案都给出了比较两个对象列表的确切解决方案。上述大多数方法仅有助于遵循比较限制 - 大小比较 - 参考比较
但是,如果我们在对象级别上有相同大小的对象列表和不同的数据,那么这种比较方法将无济于事。
我认为以下方法将与覆盖用户定义对象上的 equals 和 hashcode 方法完美配合。
我使用 Xstream lib 来覆盖等于和哈希码,但我们也可以通过赢得逻辑/比较来覆盖等于和哈希码。
这是供您参考的示例
import com.thoughtworks.xstream.XStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
class TestClass {
private String name;
private String id;
public void setName(String value) {
this.name = value;
}
public String getName() {
return this.name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object o) {
XStream xstream = new XStream();
String oxml = xstream.toXML(o);
String myxml = xstream.toXML(this);
return myxml.equals(oxml);
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
XStream xstream = new XStream();
String myxml = xstream.toXML(this);
return myxml.hashCode();
}
}
public class XstreamCompareTest {
public static void main(String[] args) throws ParseException {
checkObjectEquals();
}
private static void checkObjectEquals() {
List<TestClass> testList1 = new ArrayList<TestClass>();
TestClass tObj1 = new TestClass();
tObj1.setId("test3");
tObj1.setName("testname3");
testList1.add(tObj1);
TestClass tObj2 = new TestClass();
tObj2.setId("test2");
tObj2.setName("testname2");
testList1.add(tObj2);
testList1.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));
List<TestClass> testList2 = new ArrayList<TestClass>();
TestClass tObj3 = new TestClass();
tObj3.setId("test3");
tObj3.setName("testname3");
testList2.add(tObj3);
TestClass tObj4 = new TestClass();
tObj4.setId("test2");
tObj4.setName("testname2");
testList2.add(tObj4);
testList2.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));
if (isNotMatch(testList1, testList2)) {
System.out.println("The list are not matched");
} else {
System.out.println("The list are matched");
}
}
private static boolean isNotMatch(List<TestClass> clist1, List<TestClass> clist2) {
return clist1.size() != clist2.size() || !clist1.equals(clist2);
}
}
最重要的是,如果您不想在 Objects 的相等检查中包含任何字段,则可以通过 Annotation (@XStreamOmitField) 忽略字段。有很多这样的注释需要配置,所以深入了解这个库的注释。
我相信这个答案将节省您确定比较两个对象列表的正确方法的时间:)。如果您对此有任何问题,请发表评论。
不定期副业成功案例分享
System.out.println(actual == expected);
将返回 false,但System.out.println(actual.equals(expected));
将返回 true。.equals(..)
而不是==
?Assert.assertEquals
正常工作,并在比较失败时输出预期和实际列表。