Перейти к основному содержимому

Autotests Guide

Цель данного гайда - зафиксировать используемую терминологию, прояснить какие типы автотестов мы используем, сколько и как их пишем

Общий подход

Наш подход весьма похож на тот, что использует Spotify но некоторые детали и термины разнятся.

Автотесты мы подразделяем на три группы:

  1. End-to-end tests
  2. Component tests
  3. Unit and Integration tests

Screenshot

End-to-end тесты

Тестируют корректность совместной работы нескольких сервисов. Сама система при этом считается черным ящиком.

В качестве входной точки используется GUI или публичное API.

Тестовые сценарии максимально близки к реальным пользовательским действиям, но такие тесты довольно ломкие. Большое влияние на их стабильность оказывают:

  • инфраструктура
  • задержки при асинхронных взаимодействиях
  • внешние зависимости
  • данные лежащие в сервисах

End-to-end тесты располагаем в отдельных репозиториях

Их написанием занимаются QA инженеры

TBD добавить ссылку на страницу посвященную деталям реализации e2e тестов в Ensi

Компонентные (сomponent) тесты

Тестируем сервис как отдельную изолированную единицу.

Используем так называемые In-process component тесты, которые размещаются в том же репозитории что и код и пишутся бэкэнд-разработчиками.

Типичный компонентный тест представляет собой следующее:

  1. Обеспечиваем необходимое состояние БД через фабрики моделей;
  2. Подменяем внешние зависимости вроде клиентов других сервисов Ensi на тестовые дубли, конфигурируем их ответы;
  3. Отправляем запрос в api/запускаем консольную команду/посылаем сообщение в потребитель очереди через внутренний роутер фреймворка чтобы ускорить тесты;
  4. Проверяем формат запроса и ответа на соответствие OpenApi спецификации для данного эндпоинта. Для Laravel эта проверка уже встроена через пакет в методы отправляющие http запросы в тестах;
  5. Проверяем ответ на корректность;
  6. Опционально проверяем, что в БД/файловом хранилище произошли нужные изменения. БД используем реальную, файловое хранилище используем локальное через Storage::fake;

В качестве фреймворка для написания таких тестов используем PEST, тесты располагаем не в tests/ а непосредственно рядом с компонентами, которые они тестируют

Для каждого эндпоинта API должен быть хотя бы один компонентный тест.

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

Запуск компонентных тестов должен быть встроен в:

  • CI/CD для сервиса, если тесты не прошли, то билд должен быть помечен как FAILED
  • набор локальных git-хуков, включенных по умолчанию

Юнит (unit) и интеграционные (integration) тесты

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

В этих тестах мы тестируем уже конкретный класс или его публичный метод, а не эндпоинт апи или консольную команду.

Если взаимодействуем и проверяем при этом БД/файловое хранилище/внешний сервис, то считаем такой тест интеграционным. Если нет - юнит тестом.

Хорошими индикаторами того, что вам необходимо покрыть метод (функцию/класс) unit/integration тестами являются:

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

Если вы покрыли такие сценарии юнит тестами, то покрывать их все поверх еще и компонентными смысла нет. Стоит ограничиться там лишь самыми базовыми.

В качестве фреймворка для написания таких тестов используем PEST, тесты располагаем не в tests/ а непосредственно рядом с классами/функциями, которые они тестируют

Запуск тестов должен быть встроен в:

  • CI/CD для сервиса, если тесты не прошли, то билд должен быть помечен как FAILED
  • набор локальных git-хуков, включенных по умолчанию

Code Coverage

В расчёте code coverage (т.е процента строк кода покрытых хотя бы одним тестом) принимают участие component, integration и unit тесты. Мы не гонимся за 100% code coverage, в большинстве случаев это непрагматично. Чем ближе к 100% тем дороже в написании становится каждый новый процент и тем меньше от него реальной ценности.

В качестве ориентиров используем гайдлайны Google

60% - приемлимо

75% - похвально

90% - образцово

Проверка на минимальный code coverage должна быть встроена в CI. Если итоговый code coverage для ветки меньше 60%, то такой билд должен быть помечен как UNSTABLE или FAILED вовсе.