当前位置: 首页 > 知识库问答 >
问题:

Spring MockMvc:以任意顺序匹配JSON对象的集合

唐宏壮
2023-03-14

我有一个APIendpoint,当使用GET调用时,它会在正文中返回一个JSON对象数组,如下所示:

[
  {"id": "321", "created": "2019-03-01", "updated": "2019-03-15"},
  {"id": "123", "created": "2019-03-02", "updated": "2019-03-16"}
]

我想用Spring MockMvc测试用例检查身体。该语句当前如下所示:

mockMvc.perform(get("/myapi/v1/goodstuff").
  andExpect(status().isOk()).
  andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)).
  andExpect(jsonPath("$.*", isA(ArrayList.class))).
  andExpect(jsonPath("$.*", hasSize(2))).
  andExpect(jsonPath("$[0].id", is("321"))).
  andExpect(jsonPath("$[0].created", is("2019-03-01"))).
  andExpect(jsonPath("$[0].updated*", is("2019-03-15"))).
  andExpect(jsonPath("$[1].id", is("1232"))).
  andExpect(jsonPath("$[1].created", is("2019-03-02"))).
  andExpect(jsonPath("$[1].updated*", is("2019-03-16")));

但是,我的API的实现并不能保证JSON对象在返回数组中的顺序。如果这是一个字符串数组,我会通过生成的匹配器来解决这个问题org.hamcrest.collection.IsIterableContainingInAnyOrder

从快速搜索中,除了我上面描述的字符串情况列表之外,我也没有找到任何与我在SO上的情况相关的东西。当然,我可以将JSON对象转换为字符串。

但是我想知道,我是否可以为JSON对象列表解决这个问题,逐个比较每个对象的每个字段(如上面的代码片段所示),但是忽略集合中对象的顺序?

更新:Zgurskyi提出了一个解决方案,可以帮助我原来的简化示例。但是,在现实生活中的实际示例中,还有 2 个输入:

    < li >字段的数量是10-20而不是3 < li >并非所有匹配器都是简单的< code>is,例如:

(更接近我的原始代码)

mockMvc.perform(get("/myapi/v1/greatstuff").
      andExpect(status().isOk()).
      andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)).
      andExpect(jsonPath("$.*", isA(ArrayList.class))).
      andExpect(jsonPath("$.*", hasSize(2))).
      andExpect(jsonPath("$[0].id", is("321"))).
      andExpect(jsonPath("$[0].did", anything())).
      andExpect(jsonPath("$[0].createdTs", startsWith("2019-03-01"))).
      andExpect(jsonPath("$[0].updatedTs", startsWith("2019-03-15"))).
      andExpect(jsonPath("$[0].name", equalToIgnoringCase("wat"))).
      andExpect(jsonPath("$[0].stringValues", containsInAnyOrder("a","b","c"))).
      andExpect(jsonPath("$[1].id", is("1232"))).
      andExpect(jsonPath("$[1].did", anything())).
      andExpect(jsonPath("$[1].createdTs", startsWith("2019-03-01"))).
      andExpect(jsonPath("$[1].updatedTs", startsWith("2019-03-15"))).
      andExpect(jsonPath("$[1].name", equalToIgnoringCase("taw"))).
      andExpect(jsonPath("$[1].stringValues", containsInAnyOrder("d","e","f"))).
      andReturn();

到目前为止,我似乎没有什么比实现自己的matcher类更好的了。

或者...我可以吗?

共有3个答案

白祺然
2023-03-14

我认为更好的解决方案可能是这样的:

.andExpect(jsonPath("$.violations", hasSize(3)))
.andExpect(jsonPath("$.violations", containsInAnyOrder(
            Map.of("field", "name", "message", "must not be empty"),
            Map.of("field", "email", "message", "must not be empty"),
            Map.of("field", "birthdate", "message", "must not be null")
          )
))

它对我有用,但我必须诚实地说,我不喜欢使用<code>映射,<code>元组 ,<编码>类别 不同的类型。

敖子安
2023-03-14

此外,还有另一种方法可以断言json,而无需使用MockMvcResultMatchers严格控制顺序

.andExpect(MockMvcResultMatchers.content().json(<json-here>, false))

通过设置strict=false,它可以进行复杂的搜索。

滕英奕
2023-03-14

您可以断言列表项字段忽略顺序:

.andExpect(jsonPath("$[*].id", containsInAnyOrder("321", "123")))
.andExpect(jsonPath("$[*].created", containsInAnyOrder("2019-03-01", "2019-03-02")))
.andExpect(jsonPath("$[*].updated", containsInAnyOrder("2019-03-15", "2019-03-16")))

另一种方法是检查响应中是否存在特定的列表项目:

.andExpect(jsonPath("$.[?(@.id == 123 && @.created == \"2019-03-02\" && @.updated == \"2019-03-16\")]").exists())
.andExpect(jsonPath("$.[?(@.id == 321 && @.created == \"2019-03-01\" && @.updated == \"2019-03-15\")]").exists())

 类似资料:
  • 编辑:我对datalist做了更多的研究,它的匹配行为似乎是不可定制的。我决定抛弃datalist并制作了自己的下拉列表和自动完成插件。 如何使用Jquery或JavaScript实现这一点?

  • 我得到了一个“放心响应”对象。尝试从响应对象转换为 JSON 时。对话成功发生,但 JSON 顺序不匹配。如果有人在这方面提供帮助,那就太好了。 转换:

  • 我需要实现的是基于单个字段(产品名称,基本上由所有可能的筛选器值组成)来匹配文档。我知道这不是最可靠的解决方案,但我只有这一个领域可以使用。 我需要能够发送搜索查询,并将该查询中的单词以任何顺序匹配到name字段(名称应包含搜索查询中的所有单词)。实际上,在这一点上,简单的效果很好,但是那里缺少的是模糊。因为我们需要的另一件事是允许用户做一些拼写错误,并且仍然获得相关的结果。 我的问题是,有没有什

  • 您可以使用下面方法配置任意的对象. 例子 14.4.配置任意对象 build.gradle task configure << { def pos = configure(new java.text.FieldPosition(10)) { beginIndex = 1 endIndex = 5 } println pos.beginIndex

  • 要抽象出来,请考虑以下示例: 我正在寻找一个断言方法,该方法在比较上述任何与时通过,但在比较上述任何与时失败。 目前,我最接近的是: 值得注意的是,以下操作也将失败,因为Apples虽然包含相同的值,但不是相同对象的实例: 在JUnit中有没有一种简单的方法来做出这样的断言?我知道我可以为对象的迭代编写一个自定义断言方法,但不知何故,这似乎是一个常见的用例,应该有一个预定义的断言方法,该方法会引发

  • 我并不是在问regex模式,而是更多地问它的捕获组。我正在尝试将匹配项与正确的捕获组相关联。例如,字符串设置到匹配器组中: 那么假设您有字符串。它匹配捕获组、和。匹配器将匹配项放入、和。是否可以设置数组中的匹配以与捕获组相同的方式放置--以这样的顺序填充数组中的空元素?本质上,我想调用匹配器数组,它返回 如果匹配器找到了字符串的所有捕获组,则该操作有效,但如果字符串缺少任何内容,则不起作用。