API-автотесты: как достичь наиболее полного покрытия?

6164eab39575377f6dc2e79d1a4b22db.png

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

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

Привет, Хабр! Меня зовут Алексей Вараксин, я специалист по автоматизации тестирования в МойОфис. Ниже я поэтапно расскажу, как достичь наиболее полного покрытия серверной логики при разработке API-тестов, используя шаблон чек-листа. Примером послужит один из наших реальных рабочих кейсов.

Практическая задача

В линейке наших продуктов есть почтовая система Mailion — клиент-серверная система, написанная по принципу так называемого «тонкого» клиента (ссылки на хабр-статьи о разработке Mailion доступны в этом посте). Клиент работает с сервером через API-методы, и вся логика работы инкапсулирована на сервере. Соответственно, мы имеем серверную API, и возникает вопрос, как наиболее эффективно и наименее трудозатратно ее протестировать.

Можно писать только UI-автотесты и надеяться, что конкретный серверный метод будет должным образом протестирован. Ведь UI-автотесты — это, по сути, имитация действий пользователя, которая помимо API затрагивает фронтовую часть приложения. При использовании UI-автотестов мы каждый раз будем задействовать множество клиентских интерфейсных скриптов, что сильно увеличит время выполнения теста. К тому же эти автотесты не позволят структурно проверить каждый API-метод по отдельности, так как один элемент интерфейса может вызывать несколько API-методов. Такой подход нас, естественно, не устраивает.

Выход — необходимы API-автотесты.

Итак, мы определились с задачей и инструментом (API-автотесты). Чтобы наш инструмент работал наиболее эффективно, нужен четкий шаблон написания тестов и контроля их покрытия — поговорим об этом подробнее.

Шаблон написания тестов

Что представляет из себя отдельный серверный API-метод (эндпоинт)? Допустим, у нас есть метод по созданию пользователя в системе. Мы имеем два элемента: инкапсулированную логику внутри метода и его интерфейс, через который можем с этим методом взаимодействовать.

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

В теле запроса мы имеем json. Чтобы наиболее полно протестировать конкретный эндпоинт, нам необходимо обязательно протестировать его бизнес-логику и проверить, как метод реагирует на отправку полей объекта в различных состояниях (например, отправка невалидного значения поля, пустого и.т.д).

5141e4314ce53fc198cf648e126cc40f.png

В статье я не рассматриваю изменение заголовков запроса и параметров, так как эти элементы в нашей почтовой системе не влияют на логику работы отдельного API-метода. Сервер ориентируется на тело, которое ему отправили. Шаблон, рассмотренный далее, используется в примере для создания покрытия POST-метода, но также может быть использован для других запросов с телом (PUT, PATCH и.т.д).

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

Разграничим проверки. У нас есть непосредственная бизнес-логика API-метода, которую необходимо проверить в первую очередь. Такие тесты имеют наивысший приоритет. Далее нам нужно проверить каждое поле в допустимых и недопустимых диапазонах значений. На каждое поле может быть наложена специфическая валидация, но её может и не быть. Соответственно, все проверки разбиваем на 2 группы:

Проверки, связанные с бизнес-логикой

Проверки, не связанные с бизнес-логикой

— логика самого API-метода

— валидация полей по значению

— валидация полей по длине

— валидация полей по значению

— проверки на пустоту

— проверки на отсутствие полей

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

Проверки, связанные с бизнес-логикой:

  1. Логика самого API-метода

  2. Валидация полей по значению

  3. Валидация полей по длине

Проверки, не связанные с бизнес-логикой:

  1. Валидация полей по значению

  2. Проверки на пустоту

  3. Проверки на отсутствие полей

Для примера возьмем абстрактный метод создания пользователя в системе — create_user.

POST create_user
{
"organization_id": "string",
"name": "string",
"main_email": "string",
"additional_email": "string",
"password": "string"
}

Запрос имеет 5 полей. Предположим по бизнес-логике:

  • у пользователя обязательно должна быть указана основная почта main_email и organization_id

  • поля main_email и password имеют специфическую валидацию на сервере

Тогда по каждому пункту таблицы тесты могут быть следующими.

Проверки, связанные с бизнес-логикой:

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

  • Валидация полей по значению. Негативные тесты: мы выходим за границы допустимых значений в полях, ограниченных специфической валидацией сервера. Допустим, отправляем в поле password пароль с недопустимым символом.

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

Валидация полей, не связанных с бизнес-логикой:

  • Валидация полей по значению. Негативные тесты: мы выходим за границы допустимых значений в полях, на которых нет специфической серверной валидации, но их диапазон значений также ограничен, допустим, алгоритмом шифрования. Например, меняем в organization_id, который является uuid, один из символов на недопустимый для uuid. То есть, здесь мы не затрагиваем непосредственно бизнес-логику, но изменяем значение так, чтобы оно вышло за границы допустимых.

  • Проверки на пустоту. Негативные тесты: мы отправляем поля с пустым значениями. Делаем это для тех полей, которые не были проверены на пустоту в предыдущих проверках. Пример проверки на пустоту — отправка пустого additional_email.

  • Проверки на отсутствие полей. Негативные тесты: мы отправляем запрос без поля, которое проверяем. В первую очередь делаем это для обязательных полей, затем для необязательных.

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

Контроль покрытия

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

Метод POST /create_user:

Проверки, связанные с бизнес-логикой

Проверки, не связанные с бизнес-логикой

логика самого API-метода

— валидация полей по значению

— валидация полей по длине

— валидация полей по значению

— проверки на пустоту

— проверки на отсутствие полей

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

Разбор на примере

Вот пример реального API-метода block_user. Метод переводит пользователя системы в статус blocked.

POST /block_user
{
"id": "string",
"reason": "string"
}

Метод POST принимает объект с 2 полями:

  • id — идентификатор блокируемого пользователя вида uuid

  • reason — описание причины блокировки пользователя, длиной не более 1024 символов

Для начала разберем бизнес-требования. Пользователь в системе может находиться в нескольких состояниях:

  • ACTIVE

  • BLOCKED

  • ARCHIVED

  • MARK_DELETED — DELETED

Согласно бизнес-требованиям этого метода, перевод в статус BLOCKED допустим только из следующих статусов:

ACTIVE –> BLOCKED
ARCHIVED –> BLOCKED
MARK_DELETED –> BLOCKED
BLOCKED –> BLOCKED

В случае попытки перевода DELETED –> BLOCKED сервер выдаст ошибку клиенту с соответствующей информацией.

3d2a54956a02ccd81de841ca706906c0.png

Начнем формировать чек-лист, используя шаблон.

Проверки, связанные с бизнес-логикой

Логика самого API-метода

Чтобы протестировать основную бизнес-логику API метода, необходимо реализовать следующие тесты перевода пользователя из одного статуса в другой.

Позитивные

Негативные

Перевод пользователя в допустимый статус:

— ACTIVE –> BLOCKED

— ARCHIVED –> BLOCKED

— MARK_DELETED –> BLOCKED

— BLOCKED –> BLOCKED

Перевод пользователя в недопустимый статус:

— DELETED –> BLOCKED

Структура позитивного теста будет выглядеть следующим образом:

  • получаем идентификатор блокируемого пользователя

  • переводим пользователя в целевой статус

  • проверяем, что пользователь в целевом статусе

Структура негативного теста будет выглядеть следующим образом:

  • получаем идентификатор блокируемого пользователя

  • переводим пользователя в целевой статус

  • убеждаемся, что сервер вернул ошибку

Таким образом мы проверяем саму суть API метода — функционал перевода в статус BLOCKED.

Валидация полей по значению

Мы имеем 2 поля. Диапазон значений поля reson на сервере никак не ограничен. А поле id имеет строго допустимый диапазон значений: в данном случае множество символов этого поля должны строго формировать идентификатор существующего пользователя, причем только один. И это строго валидируется на сервере. Изменим один из символов этого поля, чтобы он продолжал формировать допустимый идентификатор, но такого пользователя в системе не существует.

Имеем негативный тест: попытка заблокировать пользователя с несуществующим id.

Структура теста будет следующая:

POST /block_user
{
"id": "валидный по структуре, но не существующий в системе идентификатор",
"reason": "любая валидная строка в пределах 1024 символов"
}

Валидация полей по длине

Согласно бизнес-требованиям поле reason не может быть длиннее 1024 символов. То есть оно дополнительно валидируется на сервере по длине. Соответственно, нужно проверить, как отрабатывает валидация на превышение длины этого поля.

Имеем негативный тест: попытка заблокировать пользователя с превышением длины поля reason.

Структура теста будет следующая:

POST /block_user
{
"id": "идентификатор блокируемого пользователя",
"reason": "строка длинее 1024 символов"
}

Отметим в таблице контроля покрытия пункты, которые мы покрыли.

Проверки, связанные с бизнес-логикой

Проверки, несвязанные с бизнес-логикой

логика самого API-метода

валидация полей по значению

валидация полей по длине

— валидация полей по значению

— проверки на пустоту

— проверки на отсутствие полей

Для наиболее полного покрытия рассмотрим проверки, не связанные с бизнес-логикой.

Проверки, не связанные с бизнес-логикой

Валидация полей по значению

Подумаем, как мы можем изменить значения полей так, чтобы не затронув бизнес-логику метода, проверить выход за границы допустимых значений. Поле reason может принимать любые символы, соответственно, множество символов здесь ничем не ограничено. Но мы также имеем поле id. Это поле имеет стандарт uuid, следовательно, сам стандарт ограничивает множество допустимых символов в идентификаторе. Мы можем проверить, возвращает ли сервер ошибку, изменив один из символов на недопустимый для uuid.

Имеем негативный тест: попытка заблокировать пользователя, отправив невалидный uuid в запросе.

Структура теста будет следующая:

POST /block_user
{
"id": "невалидный с точки зрения структуры uuid",
"reason": "любая валидная строка в пределах 1024 символов"
}

Проверки на пустоту

Тесты на пустоту подразумевают отправку полей с пустыми значениями, например, строки нулевой длины. Поле reason может иметь нулевую длину, а значит, отправка такого значения поля является допустимым случаем. При этом поле id не может иметь нулевую длину. Добавим соответствующую проверку.

Попытка заблокировать пользователя, отправив запрос с пустым id.

Структура теста будет следующая:

POST /block_user
{
"id": "",
"reason": "любая валидная строка в пределах 1024 символов"
}

Проверки на отсутствие полей

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

Имеем 2 негативных теста.

  1. Попытка заблокировать пользователя, отправив запрос без поля id.

    • отправляем запрос POST /block_user

      POST /block_user
      {
      "reason": "любая валидная строка в пределах 1024 символов"
      }
    • убеждаемся, что сервер вернул ошибку

  2. Попытка заблокировать пользователя, отправив запрос без поля reason.

    • отправляем запрос POST /block_user

      POST /block_user
      {
      "id": "идентификатор блокируемого пользователя"
      }
    • убеждаемся, что сервер вернул ошибку

В итоге получаем следующий чек-лист:

Проверки, связанные с бизнес-логикой:

  • логика самого API-метода

  • валидация полей по значению

  • валидация полей по длине

Проверки, не связанные с бизнес-логикой:

  • валидация полей по значению

  • проверки на пустоту

  • проверки на отсутствие полей

    • попытка заблокировать пользователя, отправив запрос без поля id

    • попытка заблокировать пользователя, отправив запрос без поля reason

В нашем чек-листе 11 тестов. Таблица позволяет убедиться, что мы покрыли все пункты.

Проверки, связанные с бизнес-логикой

Проверки, не связанные с бизнес-логикой

логика самого API-метода

— валидация полей по значению

— валидация полей по длине

валидация полей по значению

— проверки на пустоту

— проверки на отсутствие полей

Заключение

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

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

А если вы специалист в поиске работы, предлагаем ознакомиться с нашими вакансиями (1, 2): нам в МойОфис всегда требуются опытные автотестировщики.

© Habrahabr.ru