ChatGPT解决这个技术问题 Extra ChatGPT

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

如何在 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 结合使用。
@Thibstars - 我赞成将其作为答案。
assertArrayEquals 要求您从列表中获取数组。您可以使用 assertIterableEquals 直接对列表进行操作
assertIterableEquals 可用于 jUnit5 @ThetaSinner

d
djeikyb

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


System.out.println(actual == expected); 将返回 false,但 System.out.println(actual.equals(expected)); 将返回 true。
@Catfish 是的,这很令人困惑,不是吗。我想我是在证明匹配器使用的是 .equals(..) 而不是 ==
这比 assertEquals 好多少?
@Raedwald 断言失败时的输出。我会稍后再回来编辑差异。 hamcrest 匹配器可以生成详细的失败消息。 junit 可以简单地以类似的优点重载 assertEquals 。但主要是 junit 提供核心单元测试功能,而 hamcrest 提供了一个不错的对象差异描述器库。
@djeikyb,Assert.assertEquals 正常工作,并在比较失败时输出预期和实际列表。
j
jrmullen

不要转换为字符串并进行比较。这对性能不利。在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() 版本不是最好的方法。
M
Michał Krzywański

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

升级到 JUnit5 要简单得多。 Migration Instructions 发件人:testImplementation("junit:junit:4.13") 收件人:testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0")
d
davidxxx

其他答案中提出的来自 JUnit4/JUnit 5 的 assertEquals(Object, Object) 或来自 Hamcrest 的 assertThat(actual, is(expected)); 只有在 equals()toString() 都被比较对象的类(和深度)覆盖时才有效。

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

B
Barett

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


所以你的意思是 expected.equals(a) 将负责断言列表所持有的对象?
From List javadoc:比较指定对象与此列表是否相等。当且仅当指定对象也是一个列表时返回 true,两个列表具有相同的大小,并且两个列表中所有对应的元素对都相等。 (如果 (e1==null ? e2==null : e1.equals(e2)) 两个元素 e1 和 e2 相等。)换句话说,如果两个列表以相同的顺序包含相同的元素,则它们被定义为相等.此定义确保 equals 方法在 List 接口的不同实现中正常工作。
唉,这提供的错误消息帮助不大。我发现编写一个执行循环的实用程序类更好,这样您就可以看到哪些元素不同。
@mlk,也许,但我很犹豫是否要为这样的事情编写自定义实用程序方法。我刚才编辑的错误信息呢?
@mlk我同意编写一个循环来测试每个元素可能会更好,这样您就可以确切地知道有什么不同。这取决于列表中的对象类型。如果它们是字符串,那么只需说 "a="+a 应该没问题,但如果它们是复杂对象(其他列表,或者没有好的 toString 实现的东西),单独测试它们可能更容易
V
Viktor Nordling

如果您不关心元素的顺序,我建议在 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>

注意:如果你不关心元素的顺序,你应该使用 Set 或 Collection,而不是 List。
我同意。这个图书馆很恶心。为什么 ListAssert.assertEquals() 会默认为无序?
D
Dhyan Mohandas

您可以在 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()) ; } 返回假; }


Assert.assertEquals 中参数的顺序是预期结果,然后是实际结果,因此在您的代码段中它应该是 Assert.assertEquals(expected, actual);
A
Arun Pratap Singh

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

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

S
Sergey Vyacheslavovich Brunov
List<Integer> figureTypes = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2
                            ));

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

assertTrue(figureTypes .equals(figureTypes2 ));

K
Kh.Taheri

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

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

如果订单不匹配,这不会失败吗?
D
David Good

您提到您对列表内容的相等性感兴趣(并且没有提及顺序)。因此,来自 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);

d
devmarkpro

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

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

G
Ganesa Vijayakumar

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

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

我认为以下方法将与覆盖用户定义对象上的 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) 忽略字段。有很多这样的注释需要配置,所以深入了解这个库的注释。

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