Введение в JSONassert

Введение в JSONassert

1. обзор

В этой статье мы рассмотримJSONAssert library - библиотеку, ориентированную на понимание данных JSON и написание сложных тестовJUnit с использованием этих данных.

2. Maven Dependency

Сначала добавим зависимость Maven:


    org.skyscreamer
    jsonassert
    1.5.0

Пожалуйста, проверьте последнюю версию библиотекиhere.

3. Работа с простыми данными JSON

3.1. Использование режимаLENIENT

Давайте начнем наши тесты с простого сравнения строк JSON:

String actual = "{id:123, name:\"John\"}";
JSONAssert.assertEquals(
  "{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT);

Тест пройдет как ожидаемая строка JSON, а фактическая строка JSON совпадает.

Сравнениеmode LENIENT means that even if the actual JSON contains extended fields, the test will still pass:

String actual = "{id:123, name:\"John\", zip:\"33025\"}";
JSONAssert.assertEquals(
  "{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT);

Как мы видим, переменнаяreal содержит дополнительное полеzip, которого нет в ожидаемомString. И все же тест пройден.

Эта концепция полезна при разработке приложений. Это означает, что наши API могут расти, возвращая дополнительные поля по мере необходимости, не нарушая существующие тесты.

3.2. Использование режимаSTRICT

Поведение, упомянутое в предыдущем подразделе, можно легко изменить, используя режим сравненияSTRICT:

String actual = "{id:123,name:\"John\"}";
JSONAssert.assertNotEquals(
  "{name:\"John\"}", actual, JSONCompareMode.STRICT);

Обратите внимание на использованиеassertNotEquals() в приведенном выше примере.

3.3. ИспользованиеBoolean вместоJSONCompareMode

Режим сравнения также можно определить с помощью перегруженного метода, который принимаетboolean вместоJSONCompareMode, гдеLENIENT = false иSTRICT = true:

String actual = "{id:123,name:\"John\",zip:\"33025\"}";
JSONAssert.assertEquals(
  "{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT);
JSONAssert.assertEquals(
  "{id:123,name:\"John\"}", actual, false);

actual = "{id:123,name:\"John\"}";
JSONAssert.assertNotEquals(
  "{name:\"John\"}", actual, JSONCompareMode.STRICT);
JSONAssert.assertNotEquals(
  "{name:\"John\"}", actual, true);

3.4. Логическое сравнение

Как описано ранее,JSONAssert выполняет логическое сравнение данных. Это означает, что порядок элементов не имеет значения при работе с объектами JSON:

String result = "{id:1,name:\"John\"}";
JSONAssert.assertEquals(
  "{name:\"John\",id:1}", result, JSONCompareMode.STRICT);
JSONAssert.assertEquals(
  "{name:\"John\",id:1}", result, JSONCompareMode.LENIENT);

Строго или нет, вышеупомянутый тест пройдет в обоих случаях.

Другой пример логического сравнения можно продемонстрировать, используя разные типы для одного и того же значения:

JSONObject expected = new JSONObject();
JSONObject actual = new JSONObject();
expected.put("id", Integer.valueOf(12345));
actual.put("id", Double.valueOf(12345));

JSONAssert.assertEquals(expected, actual, JSONCompareMode.LENIENT);

Прежде всего следует отметить, что мы используемJSONObject вместо String, как мы это делали в предыдущих примерах. Следующее, что мы использовалиInteger дляexpected иDouble дляactual. Тест будет проходить независимо от типов, потому что логическое значение 12345 для них обоих одинаково.

Даже в случае, когда у нас есть представление вложенных объектов, эта библиотека работает довольно хорошо:

String result = "{id:1,name:\"Juergen\",
  address:{city:\"Hollywood\", state:\"LA\", zip:91601}}";
JSONAssert.assertEquals("{id:1,name:\"Juergen\",
  address:{city:\"Hollywood\", state:\"LA\", zip:91601}}", result, false);

3.5. Утверждения с сообщениями, указанными пользователем

Все методыassertEquals() иassertNotEquals() принимают сообщениеString в качестве первого параметра. Это сообщение предоставляет некоторую настройку для наших тестовых случаев, предоставляя значимое сообщение в случае неудачных тестов:

String actual = "{id:123,name:\"John\"}";
String failureMessage = "Only one field is expected: name";
try {
    JSONAssert.assertEquals(failureMessage,
      "{name:\"John\"}", actual, JSONCompareMode.STRICT);
} catch (AssertionError ae) {
    assertThat(ae.getMessage()).containsIgnoringCase(failureMessage);
}

В случае любого сбоя, все сообщение об ошибке будет иметь больше смысла:

Only one field is expected: name
Unexpected: id

Первая строка - это сообщение, указанное пользователем, а вторая строка - дополнительное сообщение, предоставляемое библиотекой.

4. Работа с массивами JSON

Правила сравнения для массивов JSON немного отличаются по сравнению с объектами JSON.

4.1. Порядок элементов в массиве

Первое отличие состоит в том, чтоthe order of elements in an array has to be exactly same in STRICT comparison mode. Однако для режима сравненияLENIENT порядок не имеет значения:

String result = "[Alex, Barbera, Charlie, Xavier]";
JSONAssert.assertEquals(
  "[Charlie, Alex, Xavier, Barbera]", result, JSONCompareMode.LENIENT);
JSONAssert.assertEquals(
  "[Alex, Barbera, Charlie, Xavier]", result, JSONCompareMode.STRICT);
JSONAssert.assertNotEquals(
  "[Charlie, Alex, Xavier, Barbera]", result, JSONCompareMode.STRICT);

Это очень полезно в сценарии, где API возвращает массив отсортированных элементов, и мы хотим проверить, отсортирован ли ответ.

4.2. Расширенные элементы в массиве

Другое отличие состоит в том, чтоextended elements are not allowed when the dealing with JSON arrays:

String result = "[1,2,3,4,5]";
JSONAssert.assertEquals(
  "[1,2,3,4,5]", result, JSONCompareMode.LENIENT);
JSONAssert.assertNotEquals(
  "[1,2,3]", result, JSONCompareMode.LENIENT);
JSONAssert.assertNotEquals(
  "[1,2,3,4,5,6]", result, JSONCompareMode.LENIENT);

Приведенный выше пример ясно демонстрирует, что даже в режиме сравненияLENIENT элементы в ожидаемом массиве должны точно соответствовать элементам в реальном массиве. Добавление или удаление даже одного элемента приведет к сбою.

4.3. Специальные операции с массивами

У нас также есть несколько других методов для дальнейшей проверки содержимого массивов.

Предположим, мы хотим проверить размер массива. Это может быть достигнуто путем использования конкретного синтаксиса в качестве ожидаемого значения:

String names = "{names:[Alex, Barbera, Charlie, Xavier]}";
JSONAssert.assertEquals(
  "{names:[4]}",
  names,
  new ArraySizeComparator(JSONCompareMode.LENIENT));

String“\{names:[4]}” указывает ожидаемый размер массива.

Давайте посмотрим на другой метод сравнения:

String ratings = "{ratings:[3.2,3.5,4.1,5,1]}";
JSONAssert.assertEquals(
  "{ratings:[1,5]}",
  ratings,
  new ArraySizeComparator(JSONCompareMode.LENIENT));

Приведенный выше пример проверяет, что все элементы в массиве должны иметь значение от [1,5], включая 1 и 5 включительно. Если какое-либо значение меньше 1 или больше 5, вышеуказанный тест не пройден.

5. Расширенный пример сравнения

Рассмотрим вариант использования, когда наш API возвращает несколькоids, каждое из которых является значениемInteger. Это означает, что всеids можно проверить с помощью простого регулярного выражения «\d».

Вышеупомянутое регулярное выражение можно комбинировать сCustomComparator и применять ко всем значениям всехids. Если какой-либо изids не соответствует регулярному выражению, тест завершится неудачно:

JSONAssert.assertEquals("{entry:{id:x}}", "{entry:{id:1, id:2}}",
  new CustomComparator(
  JSONCompareMode.STRICT,
  new Customization("entry.id",
  new RegularExpressionValueMatcher("\\d"))));

JSONAssert.assertNotEquals("{entry:{id:x}}", "{entry:{id:1, id:as}}",
  new CustomComparator(JSONCompareMode.STRICT,
  new Customization("entry.id",
  new RegularExpressionValueMatcher("\\d"))));


«\{id:x}» в приведенном выше примере - не что иное, как заполнитель -x можно заменить чем угодно. Поскольку это место, где будет применяться шаблон регулярного выражения «\d». Посколькуid сам находится внутри другого поляentry,Customization указывает положениеid, так чтоCustomComparator может выполнить сравнение.

6. Заключение

В этой быстрой статье мы рассмотрели различные сценарии, в которых JSONAssert может быть полезен. Мы начали с очень простого примера и перешли к более сложным сравнениям.

Конечно, как всегда, полный исходный код всех обсуждаемых здесь примеров можно найтиover on GitHub.