В дефекте написано: «скидка не приходит». В ответе поле discount со значением null; разработчик отвечает «всё верно, скидки нет» и закрывает. Через неделю та же проверка краснеет на другом стенде, а там поля discount нет вообще: ответы разные, описаны одинаково.
Данные приезжают в четырёх упаковках: JSON из API, XML из старых обменов, YAML в настройках стенда, CSV в выгрузке. Смысл один, правила чтения разные — и часть промахов растёт не из логики приложения, а из того, как прочитали упаковку.
Заказ 42 на 4990 рублей, оплаченный, без промокода — в четырёх форматах. Смысл везде один, но в JSON у значения есть тип, в XML номер уехал в атрибут, в YAML пустое значение записано как «~», а в CSV его вообще не видно — про него говорит лишний разделитель в конце строки.
JSON: типы значений и две пустоты
Ответ API выглядит как текст, а проверять надо по значениям — и решают часто кавычки.
Видов значений шесть: объект в фигурных скобках, массив в квадратных, строка в кавычках, число без кавычек, true/false и null. Одинарные кавычки и запятая после последнего поля запрещены. Разница между "42" и 42 не косметическая: строка сортируется по алфавиту ("10" раньше "9") и на приёме часто отвергается. Типов для даты и денег нет: дату шлют строкой "2026-03-01T10:00:00Z", сумму — числом, а рубли это или копейки — сказано в описании сервиса.
Вложенность даёт путь к значению: order.customer.name, у массива в пути появляется номер — и если порядок сервис не обещал, проверка «первый элемент — тот самый» будет плавать.
Пустота бывает двух сортов, и это разные истории.
живой пример
import json
ответ = json.loads('{"id": 42, "discount": null}')
print("discount" in ответ, ответ.get("discount"))
print("comment" in ответ, ответ.get("comment"))
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →
Выведет True None и False None. Поле discount есть и пусто — скидку посчитали и получили «ничего». Поля comment нет вовсе: другая версия, не хватило прав, не заполнено. Обращение по ключу даёт в обоих случаях одно и то же, поэтому разницу легко потерять — в баг-репорте её называют словами.
JSON Schema: обязательные поля списком
«Поле phone обязательное?», «какие значения у статуса?» — по памяти на это не отвечают. Ответ лежит в схеме — описании формы ответа.
{
"type": "object",
"required": ["id", "total", "status"],
"properties": {
"id": {"type": "integer"},
"total": {"type": "number", "minimum": 0},
"status": {"enum": ["NEW", "PAID", "CANCELLED"]}
}
}
Для проверок это готовый материал. required — негативные проверки: убрать поле и ждать отказа. type — разница между 42 и "42". enum — классы эквивалентности без выдумывания: три значения внутри и любое четвёртое снаружи. minimum — граница со значениями −1, 0 и 1. Схему не ищут отдельно: она внутри описания OpenAPI, и Postman сверяет с ней ответ прямо в проверке.
Чего схема не умеет: {"id": 42, "total": 0, "status": "PAID"} ей соответствует полностью, а по делу это оплаченный заказ на ноль рублей. Форму ловит схема, смысл — тестировщик.
JSONPath: адрес поля
Когда ответ вложен на три уровня, поле надо как-то назвать: в отчёте, в проверке, в разговоре. Для этого есть JSONPath: $.orders[0].customer.name. Здесь $ — корень, точка — шаг вглубь, [0] — элемент массива по номеру, [*] — все сразу: $.orders[*].total соберёт все суммы. Тот же путь понимают Postman и jq, и расплывчатое «имя пустое» становится точным адресом.
XML: атрибут или элемент
Тот же заказ в XML вдвое длиннее, и одно значение записывается двумя способами.
<order id="42">
<total currency="RUB">4990</total>
</order>
id и currency — атрибуты, total — элемент. Атрибут хранит одно значение и ничего не вкладывает; элемент может повторяться и содержать другие. Что чем быть, решил сам сервис, а путь к значению выходит разный: /order/@id против /order/total.
Префикс вроде soap: или ns2: — пространство имён, объявленное рядом через xmlns, и он часть имени: <ns2:total> и <total> для разборщика разные элементы. Отсюда «глазами поле вижу, а проверка говорит, что его нет».
XML строже JSON: незакрытый тег — не потерянное поле, а нечитаемый ответ целиком. Схема для XML называется XSD.
YAML: где он врёт молча
YAML читают как «просто текст с двоеточиями», а в нём живут настройки стендов, docker-compose и сценарии сборки. Цена ошибки — не упавший разбор, а тихо другое значение.
database:
host: db.test
port: 5432
ssl: no
version: 1.0
Вложенность задают отступы, и только пробелами — табуляция запрещена. Сдвинутая на два пробела строка уезжает под соседний ключ, а файл остаётся правильным: ошибки не будет, будет другая настройка.
ssl: no — не строка «no», а false: разборщики версии 1.1, а их большинство, читают yes, no, on, off как булево. На том же месте спотыкается код страны NO — Норвегия превращается в false. И version: 1.0 — число, равное 1, со строкой "1.0" оно не совпадёт. Лечится одинаково, кавычками: "no", "1.0".
CSV: почему выгрузка ломается
Выгрузку открывают в Excel: вместо таблицы одна колонка, вместо фамилий «РЎРјРёСЂРЅРѕРІ», артикул 4,99E+11. Причин три, и все не про приложение.
Первая — разделитель. Имя формата обещает запятую, её же описывает и RFC 4180, но соблюдать этот документ никто не обязан: Excel в русской локали ждёт точку с запятой, потому что запятая занята под дробную часть. О разделителе договариваются заранее.
Вторая — кавычки. Значение с разделителем, переводом строки или кавычкой внутри берут в двойные кавычки, а саму кавычку удваивают.
id;name
42;"ООО ""Ромашка"", Москва"
Во второй строке два поля: 42 и ООО "Ромашка", Москва — запятая внутри кавычек осталась данными. А выгрузка, собранная склейкой через ; без кавычек, съедет на колонку на первом же адресе с запятой.
Третья — кодировка. UTF-8 без BOM Excel на русской Windows читает как cp1251: те же байты складываются в другие буквы, и «Смирнов» превращается в «РЎРјРёСЂРЅРѕРІ». Помогает BOM в начале файла или импорт через «Данные → Из текста».
Excel и сам правит значения: 00123 становится 123, 1-2 — датой. Поэтому «сломались артикулы» сначала смотрят текстовым редактором: цел файл — испорчен показ, а не данные.
Где какой формат
Формат выбирает не тестировщик, а система на том конце — но по источнику он предсказуем.
| Формат | Где встретится | Чем открыть |
|---|---|---|
| JSON | ответы REST и GraphQL, журналы | Postman, DevTools, jq |
| XML | SOAP, банки и госсервисы | SoapUI, Postman |
| YAML | настройки стендов, docker-compose | редактор с подсветкой |
| CSV | отчёты, прайсы, справочники | текстовый редактор, потом Excel |
Коротко
- В JSON тип виден по записи:
"42"— строка,42— число; типов для даты и денег нет. - Поле со значением
nullи отсутствующее поле — два разных ответа, и зовут их по-разному. - JSON Schema даёт проверки:
required— негативные,type— типы,enum— классы эквивалентности,minimum— границы; смысл не ловит. - В XML значение бывает атрибутом или элементом, путь разный; префикс — часть имени.
- YAML ошибается молча: отступ меняет владельца ключа,
noстановитсяfalse,1.0— числом; лечат кавычками. - CSV ломается на разделителе, кавычках и кодировке — файл сперва смотрят текстовым редактором.
Что почитать дальше
- Какие бывают API — в каком стиле какой формат приезжает.
- API-тестирование в Postman — разобрать ответ по полям.
- Техники тест-дизайна — что вырастает из
enumи границ. - SQL для тестировщика — те же данные напрямую из базы.