Ку! Меня зовут Евген, и я Автоматизатор тестирования на Python. В этой статье я расскажу как из вопроса ко мне "на сколько % у нас покрыта API автотестами?" Я выдал базу в виде регламента по автоматизации API.

Введение

В один прекрасный июльский день ко мне приходит менеджер и спрашивает:

  • М: - а на сколько наши апи покрыты автотестами?

  • Я: - вот наша документация по тестированию, там все тест-кейсы есть.

  • М: - АЭэ, а можна это всё один документ? В эксельник конечно же. А то неудобно и непонятно.

В один прекрасный сентябрьский день ко мне приходит менеджер и спрашивает:

  • М: - а на сколько тот документ актуален?

  • Я: - на июль.

  • М: - надобно актуализировать.

В один прекрасный ноябрьский день ко мне приходит менеджер и спрашивает:

  • М: - а на сколько % у нас покрыта API автотестами?

  • Я: о_О (что есть 100%?) -

Тут я понял что так продолжаться больше не может. И решил описать в первую очередь для себя, что же можно назвать 100% покрытием? Для начала мы разберем основные подходы к покрытию автотестами API сервиса: что покрывать, как покрывать, какие типы тестов необходимы для определенного типа метода и т.п.. Как оценивать покрытие и определение для утверждений "метод покрыт" и "100% покрытие".

Как прокрывать API автотестами? 

Для ответа на этот вопрос, я решил создать схему(структуру) где все разделено на типы, подтипы и взаимосвязи. А именно:

  • Разбить методы на подтипы для определения набора доступных тест-кейсов.

  • Разбить тест-кейсы на типы проверок. 

  • Разбить шаги тест-кейса на типы для определения требуемого набора в конкретном случае. 

Подтипы методов

Опытным путём были выявлены и сформированы основные подтипы методов которые можно применить к любой структуре запроса-ответа. 

подтипы методов
подтипы методов
  • GET STATIC OBJECT - метод GET на статичный объект, данные которые никогда не должны меняться.

  • GET DINAMIC OBJECT - метод GET на динамический объект который может быть создан, отредактирован, удалён.

  • GET LIST OBJECTS - метод GET на список объектов который возвращает множество объектов, может пополняться и сокращаться, фильтроваться, сортироваться и иметь пагинацию.

  • POST CREATE OBJECT - метод POST для создание объекта. Может иметь зависимости из других сервисов/объектов, например: id черновика - они являются обязательными для создания объекта.

  • POST UPLOAD FILE - метод POST для загрузки файлов. Может иметь ограничения на вес файла и формат (mime types). А так же разновидность типа передачи данных - json, form-data.

  • POST CREATE OBJECT TO OBJECT - метод POST для создание объекта для объекта. Например лайк или комментарий к посту.

  • POST FILTER/SORT/OBJECTS ON LIST - метод POST для возврата списка объектов. Возвращает множество объектов, может пополняться и сокращаться, фильтроваться, сортироваться и иметь пагинацию.

  • PATCH OBJECT - метод PATCH для обновления объекта.

  • PATCH ELEM ON OBJECT - метод PATCH для обновления элемента(ов) объекта.

  • PUT OBJECT - метод PUT для обновления объекта.

  • DELETE OBJECT - метод DELETE для удаления объекта.

Типы тест-кейсов

Выявлены и сформированы основные типы тест-кейсов которые применимы в разных конфигурациях(в зависимости от особенностей реализации) для каждого метода и его подтипа. Описание каждого типа тест-кейса отображена в таблице ниже. 

Столбцы в таблице ниже

  • TestCaseType - тип тест-кейса который применим к перечню методов.

  • Sub.Methods - подтип метода, к которому применим тест-кейс. 

  • Priority - приоритет тест-кейсов для реализации, не путать с приоритетом эндпоинта к покрытию.

TestCaseType

Sub.Methods

Priority

TC: RequsetDefault

Дефолтный тест-кейс, проверяет работоспособность эндпонита и корректность его данных для дальнейшего тестирования.

Является первым и самым приоритетным тестом.

Steps:

1. StatusCode
2. ResponseCompareBodySwagger
3. ResponseValidateBodyScheme

GET STATIC OBJECT
GET DINAMIC OBJECT
GET LIST OBJECTS
POST CREATE OBJECT
POST CREATE OBJECT TO OBJECT
POST FILTER/SORT/OBJECTS ON LIST
PATCH OBJECT
PATCH ELEM ON OBJECT
PUT OBJECT
DELETE OBJECT

1

TC:  RequestCompareBenchmark

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

Steps:

1. StatusCode
2. ResponseCompareBodySwagger
3. ResponseValidateBodyScheme
4. GetElement\s
5. CompareBenchmark/CompareBoundary

GET STATIC OBJECT
GET DINAMIC OBJECT
GET LIST OBJECTS
POST CREATE OBJECT
POST CREATE OBJECT TO OBJECT
POST FILTER/SORT/OBJECTS ON LIST
PATCH OBJECT
PATCH ELEM ON OBJECT
PUT OBJECT
DELETE OBJECT

1

TC:
RequestPermissions

Тест-кейс проверки ролевой модели и прав доступа к данным. Покрытие эндпоинтов админки и эндпоинтов доступа к личным данным пользователей.

Steps:

1. StatusCode
2. ResponseCompareBodySwagger
3. ResponseValidateBodyScheme
4. Optional [GetElement\s]
5. Optional [CompareBenchmark/CompareBoundary]

SubTestCases:

- Sub.TC: Incorrect credentials -запрос с некорректными данными аутентификации
- Sub.TC: No credentials - запрос без данных аутентификации
- Sub.TC: Access denied[Object] - запрос с недостатком прав к объекту
- Sub.TC: Access denied[ServiceEndpoint] - запрос с недостатком прав к эндпоинту
- Sub.TC: Correct credentials[Object] - запрос с правами к объекту
- Sub.TC: Correct credentials[ServiceEndpoint] - запрос с правами к эндпоинту

GET STATIC OBJECT
GET DINAMIC OBJECT
GET LIST OBJECTS
POST CREATE OBJECT
POST CREATE OBJECT TO OBJECT
POST FILTER/SORT/OBJECTS ON LIST
PATCH OBJECT
PATCH ELEM ON OBJECT
PUT OBJECT
DELETE OBJECT

1

TC: RequestNewObject

Тест-кейс проверки для нового, созданного объекта. Этот тест-кейс зависит от метода создания и реализуется совместно или в зависимости от тест-кейса создания.

Steps:

1. StatusCode
2. ResponseCompareBodySwagger
3. ResponseValidateBodyScheme
4. GetElement\s
5. CompareBenchmark/CompareBoundary

SubTestCases:

- Sub.TC: RequsetDefault - дефолтный тест-кейс для проверки доступности и корректности нового объекта.
- Sub.TC: RequstEnvList - проверка нового объекта в необходимых окружениях.
- Sub.TC: RequestCompareBenchmark - детальная проверка нового объекта.
- Sub.TC: RequestPermissions - проверка нового объекта на предмет ограничений доступа.

GET DINAMIC OBJECT
GET LIST OBJECTS
POST FILTER/SORT/OBJECTS ON LIST

1

TC: RequestUpdateObject

Тест-кейс проверки для обновленного объекта который был создан или существовал. Этот тест-кейс зависит от метода изменения и реализуется совместно или в зависимости от тест-кейса изменения. Применим и для проверки сброса кэша.

Steps:

1. StatusCode
2. ResponseCompareBodySwagger
3. ResponseValidateBodyScheme
4. GetElement\s
5. CompareBenchmark/CompareBoundary

SubTestCases:

- Sub.TC: RequsetDefault - дефолтный тест-кейс для проверки доступности и корректности отредактированного объекта.
- Sub.TC: RequstEnvList - проверка отредактированного объекта в необходимых окружениях.
- Sub.TC: RequestCompareBenchmark - детальная проверка отредактированного объекта.
- Sub.TC: RequestPermissions - проверка отредактированного объекта на предмет ограничений доступа.

GET DINAMIC OBJECT
GET LIST OBJECTS
POST FILTER/SORT/OBJECTS ON LIST

1

TC: RequsetIncorrectBody

Тест-кейс проверки корректного ответа(ошибки) при попытке прокинуть данные в некорректном для API формате. Включает в себя перебор стандартных проверок. Главным показателем является отсутствие чувствительных данных в ответе.

Steps:

1. StatusCode
2. ResponseCompareBodySwagger
3. ResponseValidateBodyScheme

POST CREATE OBJECT
POST CREATE OBJECT TO OBJECT
POST FILTER/SORT/OBJECTS ON LIST
PATCH OBJECT
PATCH ELEM ON OBJECT
PUT OBJECT

1

TC: RequstEnvList

Тест-кейс проверки доступности объекта в разных окружениях где он должен выводится в соответствии с ожиданиями, например: админка, публичка(если опубликовано).

Steps:

1. StatusCode
2. ResponseCompareBodySwagger
3. ResponseValidateBodyScheme
4. Optional [GetElement\s]
5. Otiponal [CompareBenchmark/CompareBoundary]

GET STATIC OBJECT
GET DINAMIC OBJECT
GET LIST OBJECTS
POST CREATE OBJECT
POST CREATE OBJECT TO OBJECT
POST FILTER/SORT/OBJECTS ON LIST
PATCH OBJECT
PATCH ELEM ON OBJECT
PUT OBJECT
DELETE OBJECT

2

TC: RequestsParams

Тест-кейс проверки отправки запроса с параметрами фильтрации, сортировки и пагинации. В рамках тест-кейса проверяется корректный вывод и работоспособность параметров.

Steps:

1. StatusCode
2. ResponseCompareBodySwagger
3. ResponseValidateBodyScheme
4. GetElement\s
5. CompareBenchmark/CompareBoundary

SubTestCases:

- Sub.TC: RequestParamsFilters - проверка параметров фильтрации
- Sub.TC: RequestParamsSorts - проверка параметров сортировки
- Sub.TC: RequestParamsPaginations - проверка параметров пагинации
- Sub.TC: RequestParamsMix[Filtest/Sorts/Paginations] - проверка наборов параметров

GET LIST OBJECTS
POST FILTER/SORT/OBJECTS ON LIST

2

TC: RequsetElements

Тест-кейс проверки элемента объекта. Проверяется качество и поведение при изменениях в ключах и значениях тела запроса.

Steps:

1. StatusCode
2. ResponseCompareBodySwagger
3. ResponseValidateBodyScheme
4. GetElement\s
5. CompareBenchmark/CompareBoundary

SubTestCases:

- Sub.TC: RequestOnlyRequiredElem - проверка запроса только с обязательными элементами
- Sub.TC: Requset:BoundaryElem - проверка запроса с граничными значениями.
- Sub.TC: RequestMissingRequiredElem - проверка запросе без обязательного элемента.
- Sub.TC: RequestLengthElem - проверка запроса с проверкой лимита символов для строковых элементов.
- Sub.TC: RequestOutTypeElem - проверка запроса с некорректным типом данных для ключа
- Sub.TC: Requset:OutBoundaryElem - проверка запросе с выходом за граничные значения

POST CREATE OBJECT
POST CREATE OBJECT TO OBJECT
POST FILTER/SORT/OBJECTS ON LIST
PATCH OBJECT
PATCH ELEM ON OBJECT
PUT OBJECT

2

TC: RequestNotFound

Тест-кейс проверки на 404 ошибку. Применяется для корректного поведения ответа на несуществующие или удаленные объекты/адреса.

Steps:

1. StatusCode
2. Optional [ResponseCompareBodySwagger]
3. Optional [ResponseValidateBodyScheme]

GET STATIC OBJECT
GET DINAMIC OBJECT
GET LIST OBJECTS
POST CREATE OBJECT
POST CREATE OBJECT TO OBJECT
POST FILTER/SORT/OBJECTS ON LIST
PATCH OBJECT
PATCH ELEM ON OBJECT
PUT OBJECT
DELETE OBJECT

2

TC: RequestExtraData

Тест-кейс с "лишними" данными в запросе. Проверяется корректное поведение при добавлении этих данных в зависимости от реализации. Главным показателем является отсутствие чувствительных данных в ответе.

Steps:

1. StatusCode
2. ResponseCompareBodySwagger
3. ResponseValidateBodyScheme
4. GetElement\s
5. CompareBenchmark/CompareBoundary

SubTestCases:

- Sub.TC: RequestAddBody/AddBodyElem - проверка добавления body в ГЕТ метод или добавление элемента в тело POST/PATCH/PUT запроса.
- Sub.TC: RequetsAddHeaders - проверка добавление заголовков в запрос.
- Sub.TC: RequestAddParams - проверка добавление параметров в запрос.
- Sub.TC: Sub.TC: RequestMix[Headers/Body/Params] - проверка наборов.

GET STATIC OBJECT
GET DINAMIC OBJECT
GET LIST OBJECTS
POST CREATE OBJECT
POST CREATE OBJECT TO OBJECT
POST FILTER/SORT/OBJECTS ON LIST
PATCH OBJECT
PATCH ELEM ON OBJECT
PUT OBJECT
DELETE OBJECT

3

TC: RequsetNotAllowed

Тест-кейс проверки корректного ответа(ошибки) при попытке обратится к эндпоинту с неподдерживаемым типом метода.

Steps:

- StatusCode
- Optional [ResponseCompareBodySwagger]
- Optional [ResponseValidateBodyScheme]

GET STATIC OBJECT
GET DINAMIC OBJECT
GET LIST OBJECTS
POST CREATE OBJECT
POST CREATE OBJECT TO OBJECT
POST FILTER/SORT/OBJECTS ON LIST
PATCH OBJECT
PATCH ELEM ON OBJECT
PUT OBJECT
DELETE OBJECT

3

Шаги тест-кейсов

Выявлены и сформированы основные проверки в тест-кейсах, именуемые далее как шаг(step) которые применимы в разных конфигурациях(в зависимости от особенностей реализации) для каждого тест-кейса. 

  • Step 1: StatusCode - проверка статус кода ответа.

  • Step 2: ResponseCompareBodySwagger - сравнение тела ответа с телом примера в сваггере.

  • Step 3: ResponseValidateBodyScheme - валидация тела ответа, например с помощью pydentic.

  • Step 4: GetElemet - получение необходимых ключей и их значений из тела ответа.

  • Step 5.1: CompareBenchmark - эталонное сравнение. Подразумевает что мы получили именно то что и ожидали, конкретное значение у ключа.

  • Step 5.2: CompareBoundary - допустимое сравнение. Подразумевает что мы получили значение ключа в рамках допустимого.

Step 2 и Step 3

Разница ResponseCompareBodySwagger и ResponseValidateBodyScheme: 

  • CompareBodySwagger - непосредственное сравнение ответа и примера из сваггера.

  • ValidateBodyScheme - валидация схемы ответа с помощью модели которую описал автотестировщик в коде.

  • Причина разделения: человеческий фактор - не в 100% случаях разработчик внесет новые данные в сваггер. Ускоряет время локализации проблемы а так же закрывает необходимость ручной проверки схемы на актуальность.

Step 5

CompareBenchmark и CompareBoundary взаимозаменяемые шаги.

Как оценивать покрытие API автотестами? 

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

И как итог, ниже представлен условный список примеров с обязательным покрытием метода автотестами, что в дальнейшем мы можем назвать "100% покрытие". 

  • У метода присутствуют все тест-кейсы первого приоритета.

  • У метода присутствуют 4 тест-кейса: 2 позитивных и 2 негативных.

  • У метода присутствуют все тест-кейсы связанные с бизнес-логикой.

P.S.:

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

Комментарии (10)


  1. Tsegelnikov
    16.01.2025 07:52

    А у вас точно 100% покрытие тестами? И мем со снимаемыми трусиками. 100% покрытие это кажется что-то из серии "исчерпывающие тестирование". Кроме того, часть кейсов где есть интеграция с внешними системами или что-то связанное с платежами ну никак не покрыть автоматикой. В таких случаях остаётся уповать только на юнит-тесты.


    1. BaroH Автор
      16.01.2025 07:52

      Согласен, правда если говорить про интеграции тут всё относительно и сильно зависит от стенда, наличие тестовых сред у интеграционных сервисов и т.п. В целом, имхо, кажется и это можно привести к какому-то единому подходу.


  1. Litovsky83
    16.01.2025 07:52

    Добрый день! Вы тест кейсы создаёте руками ?


    1. BaroH Автор
      16.01.2025 07:52

      На данный момент ответ будет - да, но я работаю над автономностью всего процесса).


      1. Litovsky83
        16.01.2025 07:52

        А если щайти со стороны сваггера и вызовов в api тестах и там проверять, что мы покрыли?


        1. BaroH Автор
          16.01.2025 07:52

          Тут мы упираемся в следующее:

          • как понять что был сделан вызов в конкретный эндпоинт - просто спарсить одной функцией не получится... попадается очень длинные и напичканые всем подряд эндпоинты, котоыре нужно сначала индифицировать.

          • как понять что сделанный вызов делал конкретную проверку

          • как понять какой у нас исчерпывающий список необходимого покрытия

          Вообще не знаю, мне пока стыдно показывать да и не хотелось бы чтоб восприняли как рекламу. Я наверно отдельную статью сделаю как закончу: есть у меня фреймворк partest - можно в pypi найти. Он занимается отслеживанием покрытия на основе сваггера, в настройки можно указать название тест-кейсов которые участвуют в "100% покрытии" и исключения.


          1. Litovsky83
            16.01.2025 07:52

            Мы же можем отслеживать, по каким путям ходим (через тот же requests) и какой результат получаем? От этого можно оттолкнуться.

            Про покрытия и проверки. Для первой итерации можно зайти с другой стороны - смотреть на статус коды. И 200 будут для нас приоритетным и главными(happy path). И так же проверять условные остальные 401/403 и 400. Или можно составить список, какие статус коды мы ожидаем от эндпоинта и их проверять (это и будет покрытие).

            Во второй итерации уже можно так же смотреть на обязательные/необязательные и типы полей. И строить покрытие через это.

            Писать руками тс по покрытию api тестов это как будто не про автоматизацию.


            1. BaroH Автор
              16.01.2025 07:52

              Мы же можем отслеживать, по каким путям ходим (через тот же requests) и какой результат получаем? 

              Можем конечно. Просто это не всегда тривиально из-за особенностей реализации от сваггера к сваггеру. Тут уже вопрос к парсеру который будет это делать.

              Писать руками тс по покрытию api тестов это как будто не про автоматизацию.

              Согласен)

              И 200 будут для нас приоритетным и главными(happy path). И так же проверять условные остальные 401/403 и 400. Или можно составить список, какие статус коды мы ожидаем от эндпоинта и их проверять (это и будет покрытие).

              Сначала не понял, потом как понял! Но я пока не знаю что на это сказать, это интересная мысль.


              1. Litovsky83
                16.01.2025 07:52

                1. BaroH Автор
                  16.01.2025 07:52

                  Там под капотом как раз таки requests и нет возможности указать локальный адрес сваггера. Для моих проектов это не подходит.