Давайте сначала познакомимся. Меня зовут Александр, и я 17 лет работаю в тестировании. В основном я занимаюсь unit/api/ui/e2e/load тестами. Мой основной стек это JS/TS/Python. Так же я преподаю в университете курс автоматизации тестирования, и меня привлекают для оценки/помощи внедрения автотестов в отделах/компаниях. И моя сегодняшняя тема касается архитектуры api тестов. Язык, на котором они написаны не важен, +/- на всех языках одинаково. Свои примеры я буду показывать на Python. Возможно, для опытных коллег я буду рассказывать очевидные вещи, но, как я написал выше, иногда я участвую в консультациях в сторонних организациях и вижу довольно много кода api тестов, проблемного кода, который был написан от мидлов до лидов. Так же я посмотрел репозитории на GitHub различных школ и ... я бы переписал). Я не ставлю целей самоутвердиться за счет других, весь код ниже будет моим. Перед тем, как начнем, давайте вспомним об одном замечательном паттерне PageObject. Его довольно успешно освоили и применяют автотестеры, но на вопрос "А про что этот паттерн и какую проблему он решает?" ,к сожалению, смогут ответить не все. Так про что этот паттерн? Про абстракции и инкапсуляцию (главные друзья). Мы выделяем в отдельные слои тесты и работу со страницами, инкапсулируем работу с драйвером.  Знания PO очень помогут нам в написании API тестов. Можно освежить свои знания здесь. Так же хотелось бы отметить, что код ниже про архитектуру, некоторые вещи я намерено упрощаю, что бы не затягивать.  Давайте попробуем написать самые простые api тесты на Python. Создадим новый проект и виртуальное окружение. Добавим библиотеку  requests для возможности отправлять наши http запросы. Так же установим ранер тестов - pytest (pytest я буду использовать только как раннер тестов, без фикстур). Тестировать мы будем магазин https://stores-tests-api.herokuapp.com, вот его сваггер - https://github.com/berpress/flask-restful-api. Напишем самые простые тесты на регистрацию пользователя: Результат: Что делает этот код? Формируем боди и с помощью библиотеки requests посылаем запрос, проверяем статус код и респонс. На всякий случай проверяем, что в респонсе uuid типа int (isinstance(...)). И принтуем полученный респонс. Стандартный код, который можно найти на ютубе, в первых строчках гугла и на курсах. И в проде автотестеров. Хороший ли этот код? Как пример нормальный, как код автотестов в вашей компании не самые лучшее решение. Давайте попробуем разобраться почему. Первое: В тестах мы используем библиотеку requests (эта библиотека отвечает за запросы). Чем это плохо? Наши тесты знают, кто их тестирует. Если завтра нам надо будет заменить requests, то его необходимо будет переписывать во всех тестах. Вспоминаем идеи PO, нам необходимо разделить тесты и вызовы api - таким образом мы уберем requests и наши тесты будет легче изменять.  Второе: Тут очевидно url "https://stores-tests-api.herokuapp.com" можно вынести в константу. Третье: Для того, что бы мы моли переиспользовать наши тесты (если запустить тесты два раза, то получим 400 ошибку, такой пользователь существует).  Для этого будем использовать библиотеку faker. Четвертое: Здесь мы проверяем респонс, какого типа данные нам вернулись (функция isinstance) Пятое: print хорош для примеров, но в больших проектах не используйте его. Для этого есть logger . Он гибче и у него больше настроек.  Начинаем править. Вынесем обращения к сервису из тестов в отдельный класс. Создадим пакет register, внутри файл api.py У нас появился класс Register, который принимает в конструкторе класса url тестируемого приложения и функция register_user, которая регистрирует новых пользователей. Обратите внимание, что появился импорт requests, в тестах его больше не будет, они теперь не знаю, кто их тестирует! Далее предлагаю добавить рандом в наши тесты и прикрутить faker. Создадим файл models.py Здесь у нас есть класс RegisterUser и функция random, которая генерирует каждый раз рандомные данные, согласно сваггеру.  Четвертый пункт можно решить множественными способами и библиотеками. Я предпочитаю пользоваться attrs/cattrs (ниже буду ссылки на примеры), но возьмем библиотеку jsonschema . Наша задача заключается в том, что бы валидировать наш респонс. Если поля/типы не совпадают с нашей схемой, то наши тесты будут выкидывать ошибку, например  Создадим пакет schemas, а внутри файл registration.py Здесь мы описали, как будет выглядеть наш респонс, какие поля обязательные и какого типа они будут. Согласно справке jsonschema для валидация нам понадобится функция validate. Предлагаю ее добавить в api Если валидация не пройдет, то мы упадем на 9 строчке. Так как у нас есть положительные и негативные тесты, то для этих сценариев необходимо будет дописывать "свои" схемы в папке  schemas. Обратите внимание, что функция register_user  теперь принимает аргумент schema, как раз для случаев, которые я описал ранее. Теперь перейдем к пятому пункту - логгирование. Само логгирование в питоне не совсем простое для понимания, но свой логгер нам писать не нужно будет, за нас это реализовано в pytest, нам необходимо его только настроить. Создадим в корне каталога файл pytest.ini: Само логгирование будет в файле api.py: Теперь посмотрим, как изменились наш тест после рефакторинга: Вот файлы в гитхабе. Что изменилось? Код стал более читаемый, мы разделили тесты от запросов и добавили валидацию ответов. Достаточно ли этого? Нет, продолжим. Обратим внимание на api.py: Мы используем внутри библиотеку requests. Да, правильно, что мы ее вынесли из тестов, но должна ли она быть в api? А что если у нас таких api файлов будет 1000 и завтра библиотеку requests будем менять на асинхронную aiohttp ? Напрашивается выделить работу с запросами в отдельный слой (и да, это очень похоже на то, что мы делаем в PO, когда прячем работы с selenium в самый подвал). Создадим файл requests.py, который спрячет работу с библиотеками, которые отвечают за запросы: Я специально назвал метод запроса custom_request, что бы не путаться с библиотекой requests.  Именно здесь мы будем отправлять запросы, изолировав выполнение от тестов и api. Теперь перепишем api: Обратите внимание, что "пропал" импорт библиотеки requests.  Вот файлы в гитхабе. Что изменилось? Мы изолировали запросы, наш код стал более гибким. Достаточно ли этого? Нет, продолжим. Давайте внимательно посмотрим на наш тест: Если его запустить под дебагом с точкой остановы на 4 строке, то выяснится, что объект response типа Response (а сам Response принадлежит библиотеке requests). У нас получилось ситуация, при который тесты знают, кто их тестирует - requests. То есть "упоминание" requests попало с самого нижнего уровня абстракции наверх в тесты. В программировании это называется "протекающие абстракции" . Чем это плохо в данном случае? Если мы поменяем requests, то нам необходимо будет менять все тесты, так как новая библиотека может не иметь атрибут status_code и метод json(), которые принадлежат библиотеке requests. Будем править, добавим в models: Этот объект мы и будем возвращать в api: А так теперь будут выглядеть наши тесты: И вот теперь все! Вот файлы в гитхабе. Мы пришли к результату: тесты не зависят от реализации, тесты не знают, кто тестируют и кто посылает запросы; легко создавать фейковые данные и логгировать результат тестов; вся архитектура стала гибкой и легко поддается рефакторингу, тесты легко и быстро поддерживать. репозиторий из статьи; пример api на Python; пример api на JS. Cервис для тренировки по написанию api тестов https://stores-tests-api.herokuapp.com и сваггер к нему https://app.swaggerhub.com/apis-docs/berpress/flask-rest-api/1.0.0 Удачи!