如何在 JUnit 测试用例中对列表进行断言?不仅是列表的大小,还有列表的内容。
答案 0 :(得分:142)
我意识到这是几年前被问过的,可能这个功能不在那时。但现在,这很容易做到:
@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
答案 1 :(得分:22)
不要变换为字符串并进行比较。这对性能不利。
在junit中,在Corematchers内部,有一个匹配器=&gt; hasItems
List<Integer> yourList = Arrays.asList(1,2,3,4)
assertThat(yourList, CoreMatchers.hasItems(1,2,3,4,5));
这是我知道检查列表中元素的更好方法。
答案 2 :(得分:19)
这是一个传统的答案,适用于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
,则两个元素e2
和(e1==null ? e2==null : e1.equals(e2))
相等。)换句话说,如果两个列表包含相同顺序的相同元素,则它们被定义为相等。此定义确保equals方法在List
接口的不同实现中正常工作。
答案 3 :(得分:7)
如果您不关心元素的顺序,我建议在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>
答案 4 :(得分:7)
assertEquals(Object, Object)
或Hamcrest中的assertThat(actual, is(expected));
仅在类中覆盖equals()
和toString()
时才有效(并且)的比较对象。
这很重要,因为断言中的相等性测试依赖于equals()
,而测试失败消息依赖于比较对象的toString()
。
对于诸如String
,Integer
之类的内置类,对于...来说没有问题,因为它们会同时覆盖equals()
和toString()
。因此,用List<String>
声明List<Integer>
或assertEquals(Object,Object)
是完全有效的。
关于此问题:您必须在类中重写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"));
}
答案 5 :(得分:3)
如果你不想建立一个数组列表,你也可以尝试这个
@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
}
答案 6 :(得分:3)
List<Integer> figureTypes = new ArrayList<Integer>(
Arrays.asList(
1,
2
));
List<Integer> figureTypes2 = new ArrayList<Integer>(
Arrays.asList(
1,
2));
assertTrue(figureTypes .equals(figureTypes2 ));
答案 7 :(得分:3)
您可以使用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());
答案 8 :(得分:2)
您可以在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; return this.getItem().equals(other.getItem()) ; } return false; }
答案 9 :(得分:1)
不要重新发明轮子!
有一个Google代码库可以为您执行此操作:Hamcrest
[Hamcrest]提供一个匹配器对象库(也称为约束或谓词),允许以声明方式定义“匹配”规则,以便在其他框架中使用。典型的场景包括测试框架,模拟库和UI验证规则。
答案 10 :(得分:1)
我知道已经有很多解决此问题的方法,但我希望通过以下操作在任何情况下声明两个列表:
assertTrue(result.containsAll(expected) && expected.containsAll(result))
答案 11 :(得分:0)
我不是这个,所有上述答案都给出了比较两个对象列表的确切解决方案。 上述大多数方法仅有助于遵循比较限制 - 尺寸比较 - 参考比较
但是如果我们在对象级别上有相同大小的对象列表和不同的数据,那么这种比较方法将无济于事。
我认为以下方法可以完全覆盖用户定义对象上的equals和hashcode方法。
我使用Xstream lib来覆盖equals和hashcode,但我们也可以通过out won logics / comparison来覆盖equals和hashcode。
以下是您的参考示例
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);
}
}
最重要的是,如果您不想在对象的相等检查中包含任何字段,则可以通过Annotation(@XStreamOmitField)忽略字段。有很多这样的注释要配置,所以要深入了解这个lib的注释。
我相信这个答案可以节省你的时间来确定比较两个对象列表的正确方法:)。如果您发现任何问题,请发表评论。
答案 12 :(得分:0)
assertEquals(expected, result);
为我工作。
由于此函数获取两个对象,因此您可以向其传递任何内容。
public static void assertEquals(Object expected, Object actual) {
AssertEquals.assertEquals(expected, actual);
}
答案 13 :(得分:0)
您提到您对列表内容的均等性感兴趣(并且未提及顺序)。因此,来自AssertJ的spring-boot-starter-test
很合适。例如,它与 // 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);
一起打包。
来自AssertJ docs ListAssert#containsExactlyInAnyOrder:
验证实际组是否完全包含给定的值,并且没有任何其他顺序。 示例:
conda create -n tmp-db
conda activate tmp-db
mamba install flask-mongoengine
mamba install -c blaze flask-mongoengine
conda list
# packages in environment at /home/dario/miniconda3/envs/tmp-db:
#
# Name Version Build Channel
_libgcc_mutex 0.1 conda_forge conda-forge
_openmp_mutex 4.5 1_gnu conda-forge
ca-certificates 2020.10.14 0
certifi 2017.1.23 py34_0 conda-forge
click 7.1.2 pyh9f0ad1d_0 conda-forge
flask 1.1.2 pyh9f0ad1d_0 conda-forge
flask-mongoengine 0.7.1 py34_0 blaze
flask-wtf 0.14.3 py_0 conda-forge
itsdangerous 1.1.0 py_0 conda-forge
jinja2 2.11.2 pyh9f0ad1d_0 conda-forge
libgcc-ng 9.3.0 h5dbcf3e_17 conda-forge
libgomp 9.3.0 h5dbcf3e_17 conda-forge
markupsafe 1.0 py34_0 conda-forge
mongoengine 0.16.3 py_1 conda-forge
ncurses 5.9 10 conda-forge
openssl 1.0.2u h516909a_0 conda-forge
pip 20.2.4 py_0 conda-forge
pymongo 3.2.2 py34_0 conda-forge
python 3.4.5 2 conda-forge
readline 6.2 0 conda-forge
setuptools 32.3.1 py34_0 conda-forge
six 1.15.0 pyh9f0ad1d_0 conda-forge
sqlite 3.13.0 1 conda-forge
tk 8.5.19 2 conda-forge
werkzeug 1.0.1 pyh9f0ad1d_0 conda-forge
wheel 0.35.1 pyh9f0ad1d_0 conda-forge
wtforms 2.3.3 py_0
xz 5.2.5 h516909a_1 conda-forge
zlib 1.2.11 h516909a_1010 conda-forge