断言Junit中的2个列表之间等于


164

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

5
我现在喜欢用assertArrayEquals。与结合使用List#toArray
Thibstars '18

@Thibstars-我会回答这个问题。
dfrankow

2
assertArrayEquals要求您从列表中获取数组。您可以使用assertIterableEquals
ThetaSinner '18

assertIterableEquals可用于jUnit5 @ThetaSinner
iec2011007

Answers:


168

我意识到这是几年前提出的,可能那时还没有。但是现在,只需执行以下操作即可:

@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


3
System.out.println(actual == expected);将返回false,但System.out.println(actual.equals(expected));将返回true。
fish鱼2014年

@Catfish是的,这很令人困惑。我想我正在演示匹配器正在使用.equals(..)而不是==
djeikyb 2014年

2
这比assertEquals更好吗?
拉德瓦尔德

1
断言失败时,@ Raedwald输出。我会稍后再尝试编辑差异。hamcrest匹配器可以生成详细的故障消息。junit可能会简单地以相似的优势重载assertEquals。但是大多数情况下,junit提供了核心的单元测试功能,而hamcrest提供了一个很好的对象差异描述程序库。
djeikyb

29

不要转换为字符串并进行比较。这对性能不好。在junit的Corematchers内部,有一个匹配器=>hasItems

List<Integer> yourList = Arrays.asList(1,2,3,4)    
assertThat(yourList, CoreMatchers.hasItems(1,2,3,4,5));

我知道这是检查列表中元素的更好方法。


2
应该是选择的答案,但要注意:您还需要验证列表中除了所需内容之外没有其他项目。也许使用:Assert.assertEquals(4,yourList.size());
yoni

或单行:assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
user1778602,18年

3
“这对表现不好。” →我不确定在编写单元测试时应该在多大程度上考虑性能……但是可以肯定的是,通过字符串toString()版本进行比较不是最好的方法。
沃伦

21

这是一个遗留答案,适用于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在对此答案的评论中提到的,两个Lists相等:

当且仅当指定对象也是一个列表时,两个列表的大小相同,并且两个列表中所有对应的元素对均相等。(如果两个元素相等e1e2则等于(e1==null ? e2==null : e1.equals(e2))。)换句话说,如果两个列表包含相同顺序的相同元素,则两个列表定义为相等。此定义确保equals方法可在List接口的不同实现中正常工作。

请参阅接口JavaDocsList


1
所以您的意思是Expected.equals(a)将负责声明该列表所持有的对象?
卡马尔

1
从列表javadoc:将指定的对象与此列表进行比较以确保相等。当且仅当指定对象也是一个列表,并且两个列表具有相同的大小,并且两个列表中所有对应的元素对相等时,才返回true。(如果(e1 == null?e2 == null:e1.equals(e2)),则两个元素e1和e2相等。)换句话说,如果两个列表包含相同顺序的相同元素,则它们被定义为相等。 。此定义确保equals方法可在List接口的不同实现中正常工作。
Paul McKenzie 2010年

1
遗憾的是,此提示所提供的信息不足。我发现编写一个执行循环的实用程序类更好,这样您就可以查看哪些元素不同。
Michael Lloyd Lee mlk 2010年

@mlk,也许,但是我很犹豫为这样的事情编写自定义实用程序方法。我刚才编辑的错误消息怎么办?
巴特·基尔斯

@mlk我同意最好编写一个循环来测试每个元素,以便您确切知道有什么不同。这取决于列表中的对象类型。如果它们是字符串,则只需说“ a =“ + a应该没问题,但是如果它们是复杂的对象(其他列表,或者没有很好的toString实现的东西),则可能更容易分别测试它们
Matt N

20

assertEquals(Object, Object)从JUnit4 / JUnit 5或 assertThat(actual, is(expected));在其他答案中提出的Hamcrest只能同时使用,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是不需要按预期声明a :它使断言更直接,代码更易读:

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

但是断言/匹配器库是必须的,因为它们确实会进一步发展。
假设现在Foo 不存储Strings而是Bars的实例。
这是非常普遍的需求。使用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"));
}

7

如果您不关心元素的顺序,则建议ListAssert.assertEquals在junit-addons中使用。

链接:http//junit-addons.sourceforge.net/

对于懒惰的Maven用户:

    <dependency>
        <groupId>junit-addons</groupId>
        <artifactId>junit-addons</artifactId>
        <version>1.4</version>
        <scope>test</scope>
    </dependency>

7
注意:如果您不关心元素的顺序,则应该使用Set或Collection,而不是List。
巴雷特

11
我同意。这个图书馆很简陋。为什么地球上的ListAssert.assertEquals()默认为无序?
Ryan

5

您可以在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;
    }

4

如果您不想建立阵列清单,也可以尝试

@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  
}

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 ));

1

不要重新发明轮子!

有一个Google代码库可以为您完成此任务:Hamcrest

[Hamcrest]提供一个匹配器对象库(也称为约束或谓词),允许以声明方式定义“匹配”规则,以用于其他框架。典型方案包括测试框架,模拟库和UI验证规则。


0

我知道已经有很多选项可以解决此问题,但是我宁愿执行以下操作以在任何顺序中声明两个列表

assertTrue(result.containsAll(expected) && expected.containsAll(result))

如果订单不匹配,这不会失败吗?
iec2011007

0

assertEquals(expected, result);为我工作。由于此函数获取两个对象,因此您可以向其传递任何内容。

public static void assertEquals(Object expected, Object actual) {
    AssertEquals.assertEquals(expected, actual);
}

-1

我不是所有上述答案都给出了比较两个对象列表的确切解决方案。上述大多数方法仅在遵循比较限制时才有帮助-大小比较-参考比较

但是,如果我们具有相同大小的对象列表,并且在对象级别具有不同的数据,则这种比较方法将无济于事。

我认为以下方法将与用户定义对象上的重写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);
      }
    }

最重要的是,如果您不想在对象的同等检查中包括任何字段,则可以通过注释(@XStreamOmitField)忽略这些字段。有许多这样配置的注释,因此请深入了解此lib的注释。

我相信这个答案将节省您的时间来确定比较两个对象列表的正确方法:)。如果您对此有任何疑问,请发表评论。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.