【问题标题】:Compare two identical json lists with objects and fields within objects in different order将两个相同的 json 列表与对象中的对象和字段以不同的顺序进行比较
【发布时间】:2017-07-26 14:08:04
【问题描述】:

我正在尝试使用 JUnit 编写单元测试,我想使用 AssertsEqual 测试一个函数,其中我有两个 JsonObjects 列表。下面是我的实际和预期的 json 字符串。

String jsonOutput = [ {
                       id: 1,
                       name: abc,
                       age: 23
                      },
                      {
                       id: 2,
                       name: efg,
                       age: 26
                      },
                      {
                       id: 3,
                       name: rst,
                       age: 26
                      }
     ]


   String jsonExpected = [ {
                            id: 2,
                            name: efg,
                            age: 26
                           },
                           {  
                            id: 1,
                            age: 23,
                            name: abc
                           },
                           {
                            id: 3,
                            name: rst,
                            age: 26
                           }
             ]

在上面的示例中,可以看出,预期字符串的对象顺序发生了变化,并且各个字段的顺序也在各个对象内发生了变化。

请在下面找到我的测试用例:

   @Test
public void testApply() {
    Command command = new Command();
    Logger logger = LoggerFactory.getLogger(Command.class.getName());
    command.set(logger, new JsonObject());
    command.apply(Buffer.buffer(TestConstants.ALL_GAPS));
    Object eventList = command.apply(Buffer.buffer(TestConstants.OCCUPIED_GAPS)); // returns a list of json objects
    List<JsonObject> expectedList = createExpectedEventList(TestConstants.EVENT_LIST);
    Assert.assertEquals(expectedList, eventList);
}

private List<JsonObject> createExpectedEventList(String eventListString) {
    JsonArray eventArray = new JsonArray(eventListString);
    List<JsonObject> eventList = new ArrayList<>();
    for (Object obj : eventArray) {
        eventList.add((JsonObject) obj);
    }
    return eventList;
}


    Assert.assertEquals(expectedList, eventList);

即使提供相同的字符串更改顺序,上述函数也会返回 false。如果字符串的内容无论顺序如何都相同,我希望此函数返回 true。

【问题讨论】:

  • 到目前为止你尝试了什么?请给我们一些代码
  • 添加了单元测试用例。
  • 虽然我了解您在此处尝试执行的操作,但我担心您遗漏了一条非常重要的信息。排序不同的 Json 数组是 not 相等的。

标签: java junit


【解决方案1】:

你检查过 JSONUnit 吗?

https://github.com/lukas-krecan/JsonUnit

它的描述是:

JsonUnit 是一个在单元测试中简化 JSON 比较的库。

如果你使用 Maven,你可以创建一个像这样的项目:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.fernandes</groupId>
    <artifactId>jsonunit.test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.8.8</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.8.8</version>
        </dependency>

        <dependency>
            <groupId>net.javacrumbs.json-unit</groupId>
            <artifactId>json-unit</artifactId>
            <version>1.25.0</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.0.0-M4</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

然后你可以在这里写一个这样的 JUnit 测试:

package com.fernandes.jsonunit.test;

import org.junit.jupiter.api.Test;

import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
import static net.javacrumbs.jsonunit.JsonAssert.when;
import static net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER;

/**
 * Created by Gil on 26/07/2017.
 */
public class ArraysTest {

    @Test
    void checkArrays() {
        String array1 = "{\"test\":[4,1,2,3]}";
        String array2 = "{\"test\":[1,2,3,4]}";
        assertJsonEquals(array1, array2, when(IGNORING_ARRAY_ORDER));
    }
}

好的,如果您不能使用 JSON 单元,您可以尝试对要比较的 JSON 列表的内容进行哈希处理,对哈希进行排序,然后比较哈希列表。下面是一个使用 Jackson、JUnit 5 和 Java 8 的简单示例:

@Test
@DisplayName("When arrays equal, but in different order - match")
void checkArraysManual() throws IOException {
    String array1 = "[{\n" +
            "\"id\": 1,\n" +
            "\"name\": \"abc\",\n" +
            "\"age\": 23\n" +
            "},\n" +
            "{\n" +
            "\"id\": 2,\n" +
            "\"name\": \"efg\",\n" +
            "\"age\": 26\n" +
            "},\n" +
            "{\n" +
            "\"id\": 3,\n" +
            "\"name\": \"rst\",\n" +
            "\"age\": 26\n" +
            "}]";

    String array2 = "[{\n" +
            "\"id\": 2,\n" +
            "\"name\": \"efg\",\n" +
            "\"age\": 26\n" +
            "},\n" +
            "{\n" +
            "\"id\": 1,\n" +
            "\"name\": \"abc\",\n" +
            "\"age\": 23\n" +
            "},\n" +
            "{\n" +
            "\"id\": 3,\n" +
            "\"name\": \"rst\",\n" +
            "\"age\": 26\n" +
            "}]";

    ObjectMapper mapper = new ObjectMapper();
    JsonNode root1 = mapper.readTree(array1);
    JsonNode root2 = mapper.readTree(array2);
    ArrayNode array1Node = (ArrayNode) root1;
    ArrayNode array2Node = (ArrayNode) root2;
    assertThat(compareArrayNodes(array1Node, array2Node), is(true));
}

private boolean compareArrayNodes(ArrayNode array1Node, ArrayNode array2Node) {
    if(array1Node.size() != array2Node.size()) {
        return false;
    }
    List<Integer> hashList1 = hashArrayNode(array1Node);
    List<Integer> hashList2 = hashArrayNode(array2Node);
    return hashList1.equals(hashList2);
}

private List<Integer> hashArrayNode(ArrayNode array1Node) {
    Iterable<JsonNode> supplier = array1Node::elements;
    return StreamSupport.stream(supplier.spliterator(), false)
            .map(Objects::hashCode).sorted()
            .collect(Collectors.toList());
}

【讨论】:

  • 谢谢,我遇到了这个库,但我需要一个不添加额外依赖项的解决方案。
  • 这有点可惜。在我看来,您的要求非常适合这个库。它可以按照您的要求处理“不考虑顺序”的列表比较。
  • 我完全同意您的解决方案,但我只能添加额外的依赖项:)
  • 我的建议是在执行断言之前按 id 对数组进行排序。这应该保证在平等的情况下成功断言。实际上这是另一个答案stackoverflow.com/a/45329517/2735286 中所建议的
  • 如何对 JsonObjects 中的字段进行排序?
【解决方案2】:

我想到了两个选项:

将它们全部放入Set,然后比较集合:

Set<Foo> a = new HashSet<>(first);
Set<Foo> b = new HashSet<>(second);
assertEquals(a, b);

如果您的列表包含重复项,这将不起作用。


或者,在比较之前对列表进行排序。只要保持一致,任何任意排序都可以工作。

List<Foo> a = Collections.sort(new ArrayList<>(first));
List<Foo> b = Collections.sort(new ArrayList<>(second));
assertEquals(a, b);

【讨论】:

  • 已添加单元测试用例,您可以看看吗?
  • 不能使用 Collections.sort 因为 'Foo' (JsonObject) 不可比较。
  • 啊。那是一种痛苦。首先将它们转换为字符串。
猜你喜欢
  • 2022-11-19
  • 1970-01-01
  • 2014-11-09
  • 2011-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多