断言等于 Junit 中的 2 个列表

IT小君   2021-09-26T06:32:32

如何在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
点击广告,支持我们为你提供更好的服务
评论(13)
IT小君

对于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

2021-09-26T06:32:32   回复
IT小君

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

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

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

2021-09-26T06:32:32   回复
IT小君

这是一个遗留答案,适用于 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

2021-09-26T06:32:33   回复
IT小君

assertEquals(Object, Object)来自 JUnit4/JUnit 5 或 assertThat(actual, is(expected));来自其他答案中提出的 Hamcrest 仅适用于两者,equals()并且toString()被比较对象的类(和深层)覆盖。

这很重要,因为断言中的相等测试equals()依赖于toString()比较对象,而测试失败消息依赖于比较对象。
对于内置类,例如String,Integer等等... 没问题,因为它们覆盖了equals()toString()所以断言List<String>List<Integer>with 是完全有效的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"));
}
2021-09-26T06:32:33   回复
IT小君

对于 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());
2021-09-26T06:32:33   回复
IT小君

如果你不关心元素的顺序,我推荐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>
2021-09-26T06:32:33   回复
IT小君

您可以在 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;
    }
2021-09-26T06:32:34   回复
IT小君

如果你不想建立一个数组列表,你也可以试试这个

@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  
}
2021-09-26T06:32:34   回复
IT小君
List<Integer> figureTypes = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2
                            ));

List<Integer> figureTypes2 = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2));

assertTrue(figureTypes .equals(figureTypes2 ));
2021-09-26T06:32:34   回复
IT小君

我知道已经有很多选项可以解决这个问题,但我宁愿执行以下操作来断言任何 oder 中的两个列表

assertTrue(result.containsAll(expected) && expected.containsAll(result))
2021-09-26T06:32:35   回复
IT小君

assertEquals(expected, result);为我工作。由于这个函数有两个对象,你可以向它传递任何东西。

public static void assertEquals(Object expected, Object actual) {
    AssertEquals.assertEquals(expected, actual);
}
2021-09-26T06:32:35   回复
IT小君

您提到您对列表内容的相等性感兴趣(并且没有提到顺序)。所以containsExactlyInAnyOrder来自 AssertJ 是一个很好的选择。spring-boot-starter-test例如,它与 一起打包

AssertJ 文档 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);
2021-09-26T06:32:35   回复
IT小君

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

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

我认为以下方法可以完美地覆盖用户定义对象上的 equals 和 hashcode 方法。

我使用Xstream lib 来覆盖 equals 和 hashcode,但我们也可以通过赢得逻辑/比较来覆盖 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);
      }
    }

最重要的是,如果您不想在 Objects 的相等检查中包含任何字段,则可以通过 Annotation (@XStreamOmitField) 忽略这些字段。有很多这样的注释需要配置,所以深入了解这个库的注释。

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

2021-09-26T06:32:35   回复