断言Junit中的List

时间:2010-07-13 11:56:40

标签: java unit-testing collections junit

如何在 JUnit 测试用例中对列表进行断言?不仅是列表的大小,还有列表的内容。

14 个答案:

答案 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接口的不同实现中正常工作。

请参阅JavaDocs of the List interface

答案 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)

其他答案中提出的JUnit4 / JUnit 5中的

assertEquals(Object, Object)或Hamcrest中的assertThat(actual, is(expected));仅在类中覆盖equals()toString()时才有效(并且)的比较对象。

这很重要,因为断言中的相等性测试依赖于equals(),而测试失败消息依赖于比较对象的toString()
对于诸如StringInteger之类的内置类,对于...来说没有问题,因为它们会同时覆盖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)

对于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());

答案 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