Иногда в процессе разработки внезапно выясняется, что привычные подходы к тестированию перестают работать: автотесты громоздкие, данные — одноразовые и неудобные, а тестовые фреймворки уже не спасают. В такой момент команда или буксует, или придумывает что-то новое.
Привет, Хабр! Меня зовут Владимир, я 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.