Рубрики
Без рубрики

Схематез: тестирование на основе собственности на схемы API

Многие компании перешли от монолитов к микросервисам для лучшей масштабируемости и более быстрого развития … помеченные Python, OpenAPI, тестирование, гипотеза.

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

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

Общение между сервисными услугами становится все более сложным, когда число участников растет, и в этой статье я хотел бы поделиться своими мыслями о проблемах использования схем в веб-приложениях и наметить некоторые способы, которыми мы можем бороться с ними.

Несмотря на то, что Open API превосходит своего предшественника, Swagger, у него есть много ограничений, а также любые спецификации схемы. Основная проблема заключается в том, что даже если объявленная схема отражает все, что подразумевается для автора, это не означает, что фактическое приложение соответствует схеме.

Есть много разных подходов, которые пытаются принести схемы, приложения и документацию в синхронизацию. Наиболее распространенными подходами являются:

  • Сохраняйте отдельное, синхронизированное вручную;
  • Генерировать схему из приложения (например, apispec );
  • Предоставьте логику приложения из объявленной схемы ( connexion ).

Ни один из этих подходов не гарантирует совпадение 1: 1 поведения приложения и его схемы, и для этого есть много причин. Это может быть сложное ограничение базы данных, которое не может быть выражено в синтаксисе схемы или в повсеместном человеческом факторе – либо мы забыли обновить приложение, чтобы отразить ее схему, либо наоборот.

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

Простой способ подойти к этим проблемам – проверить приложения и настроить статические линтеры для схемы (например, Zally от Заландо), что мы делаем много, но все становится все сложнее, когда вам нужно работать с сотнями услуг различных размеров.

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

Для этой цели мы решили изучить применимость тестирования на основе собственности (PBT) для открытия схем API. Сама концепция не нова. Впервые он был реализован в библиотеке Haskell под названием QuickCheck Кен Клэсен и Джон Хьюз. В настоящее время инструменты PBT реализуются в большинстве языков программирования, включая Python, наш основной бэкэнд. В примерах ниже я собираюсь использовать Гипотеза Дэвид Р. Макивер.

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

Однако Гипотеза может быстро напомнить нам, что коммутативность сохраняется только для реальных чисел:

PBT позволяет разработчикам найти много невидимых случаев, когда код не работает, как ожидалось, так как его можно применить к схемам API?

Оказывается, что мы ожидаем от наших приложений, они должны: они должны:

  • соответствовать их схемам;
  • не сбой на каком -либо входе, или недействителен;
  • иметь время отклика, не превышающее несколько сотен миллисекунд;

Соответствие схемы может быть расширено дальше:

  • Допустимый ввод должен быть принят, неверный вход должен быть отклонен;
  • Все ответы имеют ожидаемый код состояния;
  • Все ответы имеют ожидаемый заголовок типа контента.

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

Прежде всего, мы оглянулись и обнаружили, что для этого уже есть библиотека Python – Swagger-Conformance , но казалось, что это было заброшено. Нам нужна была поддержка открытого API и большей гибкости со стратегиями для генерации данных, чем Swagger-Conformance имеет. Мы также нашли недавнюю библиотеку – Гипотеза-jsonschema , построенный одним из Гипотеза Основные разработчики, Зак Хэтфилд-Доддс. Я полностью благодарен людям, которые нашли время для разработки этих библиотек. С их усилиями тестирование на Python стало более захватывающим, вдохновляющим и приятным.

Поскольку Open API основан на схеме JSON, это был близкий матч, но не совсем то, что нам нужно. Наличие этих выводов мы решили построить нашу собственную библиотеку на вершине гипотезы, Гипотеза-jsonschema а также pytest , который будет нацелен на спецификации открытого API и Swagger.

Вот как Схематезис Проект начался несколько месяцев назад в нашей команде платформы тестирования на Kiwi.com. Идея состоит в том, чтобы:

  • Преобразовать открытые определения API и чванства в схему JSON;
  • Используйте Гипотеза-jsonschema получить правильные стратегии гипотезы;
  • Используйте эти стратегии в тестах CLI и руководителей.

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

У нас еще много интересных вещей для реализации, например:

  • Генерирование неверных данных;
  • Генерация схемы из других спецификаций;
  • Генерация схемы из приложений WSGI;
  • Применение инструментов для лучшей отладки;
  • Целевой пузырь данных на основе покрытия кода или других параметров.

Даже сейчас это помогло нам улучшить наши приложения и бороться с определенными классами дефектов. Позвольте мне показать вам несколько примеров того, как работает схемы и какие ошибки он может найти. Для этой цели я создал образец проекта, который реализует простой API для бронирования, исходный код здесь – здесь – https://github.com/stranger6667/schematheses-example Анкет У него есть дефекты, которые обычно не так очевидны с первого взгляда, и мы найдем их с помощью схемы.

Есть две конечные точки:

  • Post/api/заказы – Создает новое бронирование
  • Get/api/bookings/{booking_id}/ – Получите бронирование по id

Для остальной части статьи я предполагаю, что этот проект работает на 127.0.0.1:8080 Анкет

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

Давайте начнем с CLI, и я постараюсь создать новое бронирование. Модель бронирования имеет всего несколько полей, которые описаны в следующей схеме и таблице баз данных:

Хэндлеры и модели :

Вы заметили недостаток, который мог бы сбить приложение с помощью нездоровой ошибки?

Нам нужно использовать схему против конкретной конечной точки нашего API:

$ schemathesis run 
-M POST 
-E /bookings/ 
http://0.0.0.0:8080/api/openapi.json

Эти два варианта, -Метон и - -endpoint Позвольте вам запускать тесты только на конечных точках, которые интересны для вас.

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

File "/example/views.py", line 13, in create_booking
    request.app["db"], booking_id=body["id"], name=body["name"], is_active=body["is_active"]
KeyError: 'id'

Исправление простое, нам нужно сделать идентификатор и другие параметры, необходимые в схеме:

Давайте снова запустим последнюю команду и проверим, все ли в порядке:

Опять таки! Исключение на стороне приложения:

asyncpg.exceptions.UniqueViolationError: duplicate key value violates unique constraint "bookings_pkey"
DETAIL:  Key (id)=(0) already exists.

Кажется, что я не считал, что пользователь может попытаться создать одно и то же бронирование дважды! Тем не менее, подобные вещи распространены в производстве-дважды щелкнуть, повторить неудачу и т. Д.

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

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

Центральным элементом использования схемы схемы является экземпляр схемы. Он обеспечивает параметризацию теста, выбор конечных точек для тестирования и других параметров конфигурации.

Есть несколько способов создать схему, и все они можно найти под Schemateesis.from_ шаблон. Обычно, гораздо лучше иметь приложение в качестве пирогального приспособления, так что его можно начать по требованию (и Schematheess.from_pytest_fixture Поможет сделать так), но для простоты я буду следить за своим предположением о приложении, работающем локально на порту 8080:

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

>>> case
Case(
    path='/bookings/{booking_id}', 
    method='GET', 
    base_url='http://0.0.0.0:8080/api', 
    path_parameters={'booking_id': 2147483648}, 
    headers={}, 
    cookies={}, 
    query={}, 
    body=None, 
    form_data={}
)

Case.call делает запрос с этими данными в приложение запуска через Запросы Анкет

И тесты можно запустить с pytest (Но Unittest из стандартной библиотеки также поддерживается):

$ pytest test_example.py -v

Исключение на стороне сервера:

asyncpg.exceptions.DataError: 
invalid input for query argument $1: 2147483648 (value out of int32 range)

Выход обозначает проблему с отсутствием проверки входных значений, исправление состоит в том, чтобы указать правильные границы для этого параметра в схеме. Имея Формат: Int32 Недостаточно для проверки значения, в соответствии с спецификацией, это только намек.

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

  • Отсутствуют логику для неверных сценариев;
  • Коррупция данных (из -за отсутствия проверки);
  • Отказ в обслуживании (например, из -за плохо составленных регулярных выражений);
  • Ошибки в реализациях клиентов (из -за разрешения незарегистрированных свойств при входных значениях);
  • Другие ошибки, которые делают приложение сбоем.

Эти проблемы имеют разные уровни серьезности, однако, даже небольшие сбои забивают ваш трекер ошибок и появляются в ваших уведомлениях. Я столкнулся с некоторыми ошибками, такими как те, кто на производстве, и я предпочитаю исправлять их рано, а не разбудить Pagerduty Call среди ночи!

Есть много вещей, чтобы улучшить их в областях, и я хотел бы побудить вас внести свой вклад в схему, гипотезу, гипотезу-jsonschema или pytest, все они открыты. Вы найдете список ссылок на все эти проекты ниже.

Спасибо за Ваше внимание!:)

Дайте мне знать в комментариях любой отзыв или вопрос, который у вас могут возникнуть, или присоединиться к нашему чату на Друттер Анкет

Использованная литература:

Эта статья была первоначально опубликована в нашем блоге – https://code.kiwi.com/schematheses-property-testing-for-api-schemas-52811fd2b0a4

Оригинал: “https://dev.to/stranger6667/schemathesis-property-based-testing-for-api-schemas-3j2k”