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
, которого нет в ожидаемой String
. Тем не менее, испытание пройдет.
Эта концепция полезна при разработке приложений. Это означает, что наши API могут расти, возвращая дополнительные поля по мере необходимости, не нарушая существующие тесты.
3.2. Использование строгого
режима
Поведение, упомянутое в предыдущем подразделе, можно легко изменить, используя режим сравнения 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
в качестве первого параметра. Это сообщение обеспечивает некоторую настройку наших тестовых случаев, предоставляя значимое сообщение в случае сбоев теста:
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
'.
Приведенное выше регулярное выражение можно комбинировать с CustomComparator
и применять ко всем значениям всех идентификаторов
. Если какой-либо из id
не соответствует регулярному выражению, тест завершится ошибкой:
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"))));
« {id:x}
» в приведенном выше примере — не что иное, как заполнитель — x
можно заменить чем угодно. Так как это место, где будет применяться шаблон регулярного выражения ' \d '.
Поскольку сам id
находится внутри другой записи
поля , Customization
указывает позицию id
, чтобы CustomComparator
мог выполнить сравнение.
6. Заключение
В этой быстрой статье мы рассмотрели различные сценарии, в которых JSONAssert может быть полезен. Мы начали с очень простого примера и перешли к более сложным сравнениям.
Конечно, как всегда, полный исходный код всех обсуждаемых здесь примеров можно найти на GitHub .