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

Привет, Хабр! Меня зовут Владимир, я SDET-специалист в компании SimbirSoft. В этой статье хочу рассказать, как мы решали проблемы тестирования не традиционным написанием автотестов, а созданием собственных REST-сервисов: сервиса генерации данных и сервиса, ассистирующего тестам. В современном мире разработки программного обеспечения эффективное тестирование является одним из ключевых факторов успеха проекта. По мере сложности систем и необходимости быстро поставлять качественный продукт команды вынуждены искать новые способы оптимизации тестирования, выходящие за рамки стандартных модульных тестов или простых тестовых фреймворков.

Немного о нашей архитектуре и требованиях к автотестам

Наше приложение — это набор сервисных операций, которые, в свою очередь, представляют собой микросервисы, написанные на Java и не имеющие UI-части. Также существует ядро из n систем (около 10) и n-ое количество высокоуровневых систем (порядка 5), которые используют наши сервисы. У нас примерно 70 микросервисов, для которых необходимо автоматизировать тесты.

Кроме того, со стороны разработчиков были дополнительные требования: тесты должны быть реализованы на Java, располагаться в том же репозитории, что и сервис, а время прохождения смоука не должно превышать 3-х минут на каждый микросервис. Запуск тестов должен осуществляться по триггеру в CI/CD.

1. Актуальные проблемы в процессе тестирования

В ходе развития наших проектов мы столкнулись с рядом серьезных проблем, которые существенно влияли на эффективность процесса тестирования:

Избыточное дублирование 

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

Высокая сложность создания тестовых данных для e2e-тестов

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

Чрезмерная нагрузка на команды

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

Проблемы с управлением данными

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

2. Почему библиотека не спасла, и мы выбрали API-подход

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

Какое решение мы приняли

В итоге нашим ключевым решением стала разработка специализированных API-сервисов, автоматизирующих критичные процессы тестирования. Наш выбор был обусловлен несколькими факторами:


Централизация тестовых данных

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

При изменениях на стендах или при появлении новых фич достаточно модифицировать только сервис генерации данных — остальные системы ничего не замечают и продолжают работать в прежнем режиме. 

Унификация процессов

Подход к тестированию стал единым для всех команд. Теперь у всех есть доступ к данным, максимально приближенным к продакшену, и наше участие в тестировании сторонних систем практически не требуется.

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

Масштабируемость

Функциональность сервисов легко расширять по мере роста проекта, поэтому здесь всё понятно.

В качестве фреймворка мы выбрали Spring Boot (для Java это очевидный кандидат).
Для удобной генерации запросов использовали Feign Client (ключевым фактором была интеграция с OpenAPI).
Для хранения данных — PostgreSQL.
Для логирования — связку Log4j + Logback.

Особенности реализации довольно стандартные: генерация интеграций из OpenAPI-контрактов систем ядра, логика создания данных и набор внешних эндпоинтов для клиентов сервиса.

Мы разработали два специализированных API-сервиса

1) Генератор тестовых данных

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

2) Сервис поддержки тестов

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

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

Заключение

Все ли проблемы были решены?

Ответ: да ?

Чего нам удалось избежать?

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

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

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

Проблемы с управлением данными. Мы периодически очищаем созданные тестовые данные. Изменения в системах ядра теперь затрагивают только один сервис, а не все тесты сразу. Любой фикc можно внести одним коммитом, вместо массовых правок по всему проекту.

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

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

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

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

Спасибо за внимание!

Больше авторских материалов для SDET-специалистов от моих коллег читайте в соцсетях SimbirSoft – ВКонтакте и Telegram.

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