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

Spring MockMvc: match a collection of JSON objects in any order

我有一个 API 端点,当使用 GET 调用时,returns 正文中的 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<T>.containsInAnyOrder 生成的匹配器来解决这个问题。 但是我找不到适合我情况的匹配器 in their doc, nor any clue in the description of jsonPath method in Spring docs

通过快速搜索,除了我上面描述的 list of strings situation 之外,我也没有找到与我在 SO 上的情况相关的任何内容。 当然,我可以将 JSON 个对象转换为字符串。

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

更新Zgurskyi 一个有助于我原来的简化示例的解决方案。然而,通过一个现实生活中的实际例子,还有 2 个输入:

(更接近我的原始代码)

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

到目前为止,我似乎没有什么比实现自己的匹配器更好的了class。

或者...我可以吗?

您可以声明列表项字段而忽略顺序:

.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())

此外,还有另一种方法可以使用 MockMvcResultMatchers

断言 json 而无需严格遵守顺序
.andExpect(MockMvcResultMatchers.content().json(<json-here>, false))

通过设置strict=false,可以进行粗略的搜索。