
Как описать обязательные поля JSON и проверить сообщения об ошибках


Если у вас есть один JSON-объект заявки и нужно строго проверить, что в нем обязательно есть строковое поле request_id, удобнее всего оформить это как контракт в JSON Schema. Тогда вы сможете отделить три похожие, но разные ситуации: поле отсутствует совсем, поле передано как null, поле есть, но строка пустая. Это разные ошибки, и валидатор должен показывать их по-разному.
Ниже разберем один узкий случай: объект заявки с обязательным request_id и необязательным quantity. Заодно посмотрим, как не спутать JSON Schema с XSD, как получить понятные сообщения об ошибках и что считать проверкой структуры, а что — отдельным бизнес-правилом.
Контракт одного объекта заявки
Начать стоит с простого: мы описываем один JSON-объект, а не XML-документ и не набор таблиц. В JSON объект — это набор пар «ключ-значение», где имена ключей являются строками. Для нашей заявки нужны всего два поля: request_id и quantity. Первое обязательно, второе можно не передавать.
Здесь часто возникает путаница из-за слова «схема». JSON Schema — это не XSD. XSD применяется к XML и использует другую модель описания структуры. В JSON Schema обязательность задается через required, типы — через type, а дополнительные ограничения вроде запрета пустой строки — через отдельные ключевые слова, например minLength.
Практический смысл такого контракта в том, что проверка становится повторяемой. Вместо ручного правила «проверьте, что request_id есть и он нормальный» вы получаете формальное описание, которое одинаково понимают тесты, валидаторы и разработчики.
Схема обязательных полей и типов
Ниже — рабочая схема для диалекта JSON Schema draft 2020-12. Диалект лучше указывать явно через $schema, чтобы не спорить, по каким правилам должен работать валидатор.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"request_id": {
"type": "string",
"minLength": 1
},
"quantity": {
"type": "integer"
}
},
"required": ["request_id"],
"additionalProperties": false
}В этой схеме есть три важных решения. Первое: required требует присутствия поля request_id, но сам по себе не проверяет его содержимое. Второе: type: "string" означает, что null не подойдет, потому что null — это другой тип. Третье: minLength: 1 отдельно запрещает пустую строку "". Без этого ограничения пустой request_id формально был бы строкой и прошел бы проверку типа.
Поле quantity описано как integer, но в массив required не включено, значит оно необязательно. Объект {"request_id":"REQ-1001"} по этой схеме валиден. А вот {"request_id":"REQ-1001","quantity":"2"} невалиден: поле есть, но его тип не соответствует контракту.
Я добавил additionalProperties: false, чтобы схема отклоняла лишние поля, не описанные в контракте. Это не обязательный шаг для каждого проекта, но он помогает держать формат заявки компактным и предсказуемым.
Разбор четырех учебных объектов
Учебный пример. Возьмем четыре условных объекта и посмотрим, как именно они проходят или не проходят валидацию. Здесь важно смотреть не только на итог «валиден/невалиден», но и на смысл ошибки.
| Объект | Результат | Что именно не так |
|---|---|---|
{"quantity":2} | Невалиден | Нет обязательного поля request_id. Это ошибка по ключевому слову required. |
{"request_id":null,"quantity":2} | Невалиден | Поле присутствует, но имеет тип null, а схема требует string. Это ошибка по type. |
{"request_id":"","quantity":2} | Невалиден | Поле присутствует и тип правильный, но строка пустая. Это ошибка по minLength. |
{"request_id":"REQ-1001","quantity":2} | Валиден | Обязательное поле есть, оно непустое, quantity передан целым числом. |
Этот набор полезен тем, что различает три похожие ситуации. Когда поле отсутствует, валидатор жалуется не на тип, а на обязательность. Когда поле равно null, проблема уже не в обязательности: поле есть, но значение неправильного типа. Когда поле пустое, тип верный, но нарушено дополнительное ограничение длины. Если в тестах смешать эти случаи, сообщения об ошибках становятся расплывчатыми, а разбор дефекта — дольше.
Для читателя это дает проверяемый результат: если ваш валидатор настроен правильно, из четырех объектов ровно один должен пройти, а у трех остальных должны появиться ошибки с разными ключевыми словами. Это хороший минимальный набор для автотеста контракта.
Как проверять сообщения об ошибках в коде
Проверять нужно не только человекочитаемую фразу вроде «must be string». Полный текст сообщения часто зависит от библиотеки и даже от ее версии. Надежнее проверять структуру ошибки: ключевое слово схемы, путь к полю и параметры ошибки. Тогда тесты будут устойчивее.
Ниже пример для Node.js с Ajv 8 и диалектом 2020-12. Скрипт валидирует те же четыре объекта и выводит краткие сведения об ошибках.
import Ajv2020 from "ajv/dist/2020.js";
const schema = {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
properties: {
request_id: { type: "string", minLength: 1 },
quantity: { type: "integer" }
},
required: ["request_id"],
additionalProperties: false
};
const cases = [
{ name: "missing_request_id", data: { quantity: 2 } },
{ name: "null_request_id", data: { request_id: null, quantity: 2 } },
{ name: "empty_request_id", data: { request_id: "", quantity: 2 } },
{ name: "valid_application", data: { request_id: "REQ-1001", quantity: 2 } }
];
const ajv = new Ajv2020({ allErrors: true });
const validate = ajv.compile(schema);
for (const testCase of cases) {
const ok = validate(testCase.data);
console.log(`\n${testCase.name}: ${ok ? "VALID" : "INVALID"}`);
if (!ok) {
for (const err of validate.errors ?? []) {
console.log({
keyword: err.keyword,
instancePath: err.instancePath,
params: err.params,
message: err.message
});
}
}
}Что здесь проверять в автотесте. Для первого объекта — что среди ошибок есть keyword: "required" и в параметрах указан request_id. Для второго — keyword: "type" и путь /request_id. Для третьего — keyword: "minLength" и тот же путь /request_id. Для четвертого — что ошибок нет совсем. Такой тест не привязан к одной фразе сообщения и при этом точно указывает, какое правило нарушено.
Практический результат учебного примера простой: вы можете автоматизировать проверку контракта и сразу видеть, какая категория ошибки возникла — отсутствие поля, неверный тип или пустое значение.
Техническая валидность и бизнес-правила
Даже хорошая JSON Schema не закрывает все вопросы. Есть как минимум три уровня проверки. Первый — JSON должен быть синтаксически корректным. Второй — объект должен соответствовать схеме. Третий — данные должны соответствовать бизнес-логике системы. Эти уровни полезно не смешивать, иначе одно и то же падение теста начинают объяснять разными причинами.
Например, объект {"request_id":"REQ-1001","quantity":0} технически является корректным JSON. По нашей схеме он тоже валиден, потому что request_id — непустая строка, а quantity — целое число. Но если в вашем процессе количество должно быть только положительным, это уже отдельное правило. Его можно перенести в схему через minimum, а можно проверять отдельным шагом — в зависимости от того, считается ли это частью контракта API.
По этой же причине не стоит требовать от схемы того, что она не обязана решать. JSON Schema хорошо описывает структуру, типы и часть ограничений значений. Но уникальность request_id в базе, существование связанного объекта или допустимость идентификатора в конкретном статусе — это уже логика приложения, а не просто форма JSON.
Если хотите потренировать такие проверки на JavaScript, в сохраненном описании каталога курса по автоматизации тестирования на JavaScript есть тема про API, HTTP-запросы, валидацию ответов и работу с JSON. Официальная страница школы для проверки сейчас недоступна, поэтому ориентироваться стоит именно на сохраненное описание каталога.





















