Тестирование REST API начинается с семантики кодов состояния

REST наделяет коды состояния смыслом, и клиенты на этот смысл опираются. Его стоит проверять явно, а не считать само собой разумеющимся.

  • 201 с адресом созданного ресурса при создании, а не 200 с телом ответа и ничем больше.

  • 404 — для отсутствующего, 403 — для запрещённого. Отдавать 404, когда доступ запрещён, — осознанное решение, которое принимают некоторые команды; отдавать 403, когда ресурса нет, — просто ошибка.

  • 422 или 400 для некорректных данных, но единообразно. Выберите что-то одно и проверьте, что все эндпоинты этого придерживаются.

  • Никогда — 200 с ошибкой внутри тела ответа. Встречается часто и вынуждает каждого клиента писать особую обработку.

Идемпотентность

PUT и DELETE должны быть идемпотентными. Двойной вызов должен оставлять то же состояние и не приводить к ошибке во второй раз. Здесь легко ошибиться — и так же легко проверить: вызовите дважды и проверьте результат.

То же касается повторного DELETE, который должен возвращать 404, а не 500. Клиенты делают повторные попытки, и эндпоинт, небезопасный при повторе, превращает кратковременный сбой сети в заметный пользователю отказ.

Соглашения, которые заявляет ваш сервис

ПагинацияВозвращает ли полный обход столько записей, сколько заявляет коллекция. Ошибка на единицу на границах страниц встречается крайне часто.
Фильтрация и сортировкаОтклоняется ли неизвестный фильтр или молча игнорируется. Молчаливое игнорирование выдаёт уверенно неверные данные.
Частичное обновлениеМеняет ли PATCH только то, что было передано, или тихо обнуляет остальное.

Проверка, лежащая в основе всех остальных

Проверяйте тело ответа, а не только статус. Ответ 200 с пустым списком там, где должна быть запись, пройдёт любую проверку статуса, какую только писали. Одна эта привычка находит в тестировании REST API больше реальных дефектов, чем любая проверка соглашений.

Четыре проблемы

Переживёт ли набор API-тестов первый год, решают четыре вещи: истекающие сессии, значения, которые существуют только во время выполнения, взаимозависимые вызовы и записи, за которыми никто не убирает. TestSprite закрывает их механизмами Auto-Authentication, Dynamic Variables, Dependency Chains и Auto-Cleanup, описанными в документации по тестированию API.

Соглашения, которые стоит зафиксировать в первую очередь

Прежде чем проверять, следует ли ваш API своим соглашениям, кто-то должен сформулировать, в чём они состоят, — и большинство команд в этот момент обнаруживает, что не сходится во мнениях.

Большую часть решают четыре вопроса. Какой статус возвращает создание и есть ли в ответе адрес созданного ресурса. Отсутствующий ресурс — это 404 или 403, если вызывающему в любом случае не положено его видеть. Неизвестный параметр запроса отклоняется или игнорируется. Считает ли PATCH отсутствующее поле неизменённым или равным null.

Ни у одного из них нет универсально правильного ответа, и все они — решения, на которые опираются ваши клиенты. Записать четыре ответа — дело двадцати минут, и это превращает смутное намерение «быть RESTful» в то, что можно реально проверить; без этого ничего из перечисленного не протестировать.

Как получить покрытие, не написав всё вручную

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

Терминал

npm install -g @testsprite/testsprite-cli
testsprite setup

Если ничего устанавливать не хочется, то же самое делает панель управления. Всё остальное, что умеет командная строка, — в репозитории CLI.

Если пайплайн принадлежит другой команде, то GitHub App — путь наименьшего сопротивления: это вебхук, он ничего не меняет в вашем репозитории и срабатывает, когда сборка сообщает, что новая версия развёрнута. Если же вы хотите, чтобы проверка была видна в самом репозитории, для этого есть шаг GitHub Actions — он делает то же самое.

Как TestSprite проверяет соглашения

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

Auto-Authentication поддерживает сессии живыми на протяжении всего прогона, Dynamic Variables переносят значения между вызовами, Dependency Chains выводят порядок выполнения из того, что каждому тесту нужно и что он производит, а Auto-Cleanup удаляет ровно то, что было создано за прогон. Последнее для REST важнее, чем принято думать: проверки идемпотентности означают намеренный двойной вызов одной и той же разрушающей операции.

В итоге ваши собственные соглашения проверяются при каждом изменении, а не просто описаны в документации в надежде, что их соблюдают, — именно это не даёт одному эндпоинту тихо возвращать 200 там, где все остальные возвращают 201.

Важна ли чистота REST для тестирования?

Только там, где от неё зависят клиенты. Проверяйте те соглашения, которым ваш сервис обещает следовать, а не те, что предписывает учебник.

Как тестировать HATEOAS?

Если вы отдаёте ссылки, проверяйте, что они открываются и ведут туда, куда заявлено. Если не отдаёте — пропустите: по-настоящему это реализуют очень немногие сервисы.

А как же GraphQL?

Там совсем другие соглашения. Коды состояния несут гораздо меньше смысла, а ошибки по замыслу приходят в теле ответа, поэтому описанные выше проверки в основном не переносятся.

Стоит ли проверять заголовки ответа?

Тип содержимого — всегда. Заголовки кэширования и ограничения частоты запросов — если клиенты на них реагируют, а это стоит выяснить до того, как писать по ним проверки.

Стоит ли тестировать версионирование?

Да, если вы поддерживаете больше одной версии. Проверяйте, что старая версия ведёт себя по-прежнему, — именно это обещает номер версии.

Коротко

Проверяйте обещания, которые даёт ваш API.

Тестирование REST API должно подтверждать соглашения, которые заявляет ваш сервис: семантику статусов, идемпотентность, итоговые числа в пагинации и то, отклоняются ли неизвестные фильтры. А в основе всего этого — проверяйте тело ответа, а не статус.