«1. Обзор

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

2. Зависимость от Maven

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

<dependency>
    <groupId>org.skyscreamer</groupId>
    <artifactId>jsonassert</artifactId>
    <version>1.5.0</version>
</dependency>

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

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

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

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

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

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

Режим сравнения LENIENT означает, что даже если реальный JSON содержит расширенные поля, тест все равно пройдет:

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

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

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

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

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

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

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

3.3. Использование логического значения вместо JSONCompareMode

Режим сравнения также можно определить с помощью перегруженного метода, который принимает логическое значение вместо 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 для ожидаемого и Double для фактического. Тест будет пройден независимо от типов, потому что логическое значение 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 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. Порядок элементов в массиве

Первое отличие состоит в том, что порядок элементов в массиве должен быть точно таким же в режиме сравнения STRICT. Однако для режима сравнения 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. Расширенные элементы в массиве

Еще одно отличие состоит в том, что расширенные элементы не допускаются при работе с массивами JSON:

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

Строка «{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 возвращает несколько идентификаторов, каждый из которых является целым числом. Это означает, что все идентификаторы можно проверить с помощью простого регулярного выражения «\\d».

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

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

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

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

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

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