首页 > 解决方案 > JUnit ~ 断言该列表包含以任意顺序具有相同值的对象

问题描述

我的用例是我正在编写一个文档解析器(用于 ReqIF 文档)并且想要比较解析的对象是否包含预期的元素。为此,我想编写一个 JUnit 测试。

实际上,我正在寻找的是一种断言方法,它将两个对象列表与值进行比较,并在两个列表包含(以任何顺序)与引用列表具有相同值的对象时传递。

为了抽象,考虑这个例子:

Apple referenceApple1 = new Apple(color="red");
Apple referenceApple2 = new Apple(color="green");
Apple parsedApple1 = new Apple(color="red");
Apple parsedApple2 = new Apple(color="green");
Apple badApple = new Apple(color="brown");

List<Apple> referenceList = new List<Apple>(referenceApple1, referenceApple2);
List<Apple> correctlyParsedList1 = new List<Apple>(parsedApple1, parsedApple2);
List<Apple> correctlyParsedList2 = new List<Apple>(parsedApple2, parsedApple1);
List<Apple> wronglyParsedList1 = new List<Apple>(parsedApple1, badApple);
List<Apple> wronglyParsedList2 = new List<Apple>(parsedApple1, parsedApple2, parsedApple1);
List<Apple> wronglyParsedList3 = new List<Apple>(parsedApple2, parsedApple2);

我正在寻找一种断言方法,该方法在将上述任何内容与 . 进行比较时correctlyParsedList*通过referenceList,但在将上述任何内容wronglyParsedList*referenceList.

目前,我最接近的是:

assertEquals(referenceList.toString(), correctlyParsedList1.toString())

但是,一旦对象处于另一个顺序,这将失败。

//Will fail, but I want a method that will compare these and pass
assertEquals(referenceList.toString(), correctlyParsedList2.toString())

值得注意的是,以下内容也将失败,因为 Apple 虽然包含相同的值,但不是同一对象的实例:

assertThat(correctlyParsedList1, is(referenceList));

//Throws This error:
java.lang.AssertionError: 
Expected: is <[[color="red"], [color="green"]]>
     but: was <[[color="red"], [color="green"]]>

有没有一种简单的方法可以在 JUnit 中做出这样的断言?我知道我可以为此迭代对象编写一个自定义断言方法,但不知何故,感觉这将是一个常见的用例,应该有一个预定义的断言方法,该方法会引发表达性断言错误。


编辑~具体化

我实际上试图对这个抽象示例做的是使用 JDOM2 解析复杂的 XML,并且我想断言正在解析的标记的属性等于我作为输入提供的示例文档中存在的属性。由于这是 XML,因此属性的顺序无关紧要,只要它们具有正确的值即可。

如此有效,我在这个实际用例中比较的是两个List<Attribute>Attribute来自org.jdom2.Attribute.

我目前不满意的完整临时测试用例,因为如果属性的顺序发生变化但不应该发生变化,它将失败,如下所示:

    @Test
    public void importXML_fromFileShouldCreateXML_objectWithCorrectAttributes() throws JDOMException, IOException {
        testInitialization();
        List<Attribute> expectedAttributes = rootNode.getAttributes();

        XML_data generatedXML_data = xml_importer.importXML_fromFile(inputFile);
        List<Attribute> actualAttributes = generatedXML_data.attributes;

        assertEquals(expectedAttributes.toString(), actualAttributes.toString());
    }

我在尝试做出该断言时遇到的具体错误assertThat(expectedAttributes, is(actualAttributes))是:

java.lang.AssertionError: 
Expected: is <[[Attribute: xsi:schemaLocation="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd http://www.omg.org/spec/ReqIF/20110401/reqif.xsd"], [Attribute: xml:lang="en"]]>
    but: was <[[Attribute: xsi:schemaLocation="http://www.omg.org/spec/ReqIF/20110401/reqif.xsd http://www.omg.org/spec/ReqIF/20110401/reqif.xsd"], [Attribute: xml:lang="en"]]>

标签: javaunit-testingjunitjdom-2

解决方案


您应该使用containsAll检查一个列表是否包含另一个列表中的所有项目,只要您有适当的 equals 实现,Apple这应该可以工作:

    // Covers case where all items that are from correct list are in reference as well
    // but also some additional items exist that are not in correct list
    assertEquals(correctlyPassedList.size(), referenceList.size());
    // Checks if each element is contained in list
    assertTrue(referenceList.containsAll(correctlyPassedList));

更新

关于您的最新评论,这可能效果最好:

    // Sort both lists using comparator based on id or whatever field is relevant
    referenceList.sort(Comparator.comparingInt(Apple::getId));
    correctlyParsedList.sort(Comparator.comparingInt(Apple::getId));
    // Check if they are equal
    assertEquals(correctlyParsedList, referenceList);

推荐阅读