Spring MockMvc: сопоставить коллекцию объектов JSON в любом порядке - PullRequest
1 голос
/ 20 марта 2019

У меня есть конечная точка API, которая при вызове с помощью 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 в возвращаемом массиве.Если бы это был массив строк, я бы решил это с помощью matcher, сгенерированного org.hamcrest.collection.IsIterableContainingInAnyOrder<T>.containsInAnyOrder.Но я не вижу ни подходящего сопоставителя для моей ситуации в их документе , ни какой-либо подсказки в описании jsonPath метода в Spring docs

Из быстрого поискаМне так и не удалось найти ничего, связанного с моей ситуацией на SO, кроме списка строк ситуации , который я описал выше.Конечно, я мог бы преобразовывать объекты JSON в строки.

Но мне интересно, мог бы я решить эту проблему для списка объектов JSON, сравнивая каждое из полей каждого объекта по одному (например,показано в приведенном выше фрагменте кода), но игнорируете порядок объектов в коллекции?

Обновление : Згурский предложил решение, котороепомогает с моим оригинальным упрощенным примером.Тем не менее, на реальном практическом примере есть еще 2 входа:

  • количество полей 10-20 вместо 3
  • , не все совпадения являются простыми 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.

Или ... можно?

1 Ответ

2 голосов
/ 20 марта 2019

Вы можете утверждать поля элементов списка, игнорируя порядок:

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

...