Автоматизация тестирования, которая не ломается при первом редизайне
Как мы проектировали, внедряли и поддерживаем живую систему автотестов

Привет! Меня зовут Артем, я эксперт по тестированию в компании TData —разработчике высоконагруженных корпоративных решений для работы с данными в реальном времени. Мы создаём ПО, где особенно важны стабильность, отказоустойчивость и предсказуемость поведения — вне зависимости от нагрузки и сложности сценариев.
Автоматизация тестирования для нас — не просто способ снять нагрузку с ручных тестировщиков, а часть инженерной культуры. В этой статье поделюсь тем, как мы выстраивали автоматизацию: с чего начали, почему стартовали с UI, какие инструменты прижились, как справлялись с нестабильными тестами. Давайте разберёмся, как не заблудиться на этом увлекательном, но местами запутанном пути.
Зачем мы пишем автотесты и что они нам дают?
На старте всё кажется очевидным: «автотесты — это быстрее и лучше, чем руками». Но что это означает на практике?
Во-первых, автотесты помогают находить ошибки ещё до того, как изменения попадают в основную ветку проекта — ту, из которой собирается продукт. Если баг ловится на этапе проверки кода, это экономит время всех: разработчиков, тестировщиков и техподдержки. Это особенно важно при частых релизах. Чем раньше мы обнаружим проблему, тем легче и дешевле будет ее исправить. Регулярные автотесты помогают поддерживать высокий уровень качества, так мы можем быть уверены в стабильности приложения.
Во-вторых, они экономят ресурсы. Один UI-сценарий, который ручной тестировщик раньше прогонял каждый спринт, теперь проверяется автоматически — хоть сто раз в день. Мы высвобождаем время для более сложных задач, таких как исследовательское тестирование или создание новых тестовых сценариев.
В-третьих, автотесты убирают человеческий фактор. Машина не забывает нажать на кнопку, не перепутает шаги и не пропустит баг из-за усталости. Если тест падает — значит, с продуктом реально что-то не так. Или с тестом. Но это уже повод разобраться.
Всё это — не абстрактные плюсы, а вполне измеримые эффекты, которые мы в TData наблюдаем каждый день.
Почему начали с UI, а не с API
Мы приняли решение начать автоматизацию тестирования с UI-части. Но почему? По классической пирамиде тестирования автоматизация должна начинаться с юнит- и API-тестов. Но мы выбрали другой путь — начали с UI. Причины были вполне прагматичными. Во-первых, хотелось сразу снизить нагрузку на тестировщиков, которые вручную прогоняли одни и те же сценарии из билда в билд. Во-вторых, было важно показать менеджерам результат, который можно увидеть глазами: как скрипт сам открывает интерфейс, кликает кнопки и проходит сценарий. Это производило гораздо большее впечатление, чем любые диаграммы с ожидаемой экономией ресурсов или предполагаемом росте продуктивности.
Конечно, мы понимали, что UI-тесты более хрупкие, менее масштабируемые и требуют большего внимания. Но на старте они дали необходимый импульс и позволили быстро продемонстрировать пользу автоматизации. Спустя некоторое время, когда UI-тесты были написаны, у ручных тестировщиков стало меньше рутинной работы, мы постепенно сместили фокус на API, где архитектура уже выстраивалась более основательно.
Наш стек технологий
Для автоматизации UI тестов мы используем Selenide. В отличие от своего предшественника Selenium, Selenide проще поддерживать и развивать, особенно для тех, кто только встраивается в процесс. И, что самое главное, он быстрее. Короткий и выразительный синтаксис, встроенные ожидания, лаконичные конструкции — всё это помогло нам ускорить старт.
В качестве тестового движка мы используем JUnit 5 — современный, гибкий и хорошо совместимый с остальным стеком. А чтобы тесты не бегали на локальной машине, а масштабировались по браузерам и версиям, мы настроили запуск через Selenoid. Контейнеры, удалённое выполнение, видеофиксация прогонов — всё это дало возможность полноценно интегрировать тестирование в CI. Если вам кажется, что это звучит сложно, не волнуйтесь! Я тоже так думал в начале своего пути.
Когда перешли к API, логичным выбором стала Rest Assured. По-моему, это лучшая библиотека на Java для автоматизации API. У нее много преимуществ перед конкурентами, начиная с интуитивно понятного синтаксиса и заканчивая высокой производительностью. API-методы удобно описываются, легко поддаются сериализации через POJO, и в целом эта часть автоматизации получилась более стабильной, чем UI.
Как мы пишем UI-автотесты — и почему без Page Object быстро всё развалится
Теперь, когда мы разобрались, что лучше начинать автоматизацию с UI-тестов, давайте поговорим о том, как их писать. Если вы только начинаете путь в автоматизацию, может возникнуть соблазн писать всё в одном месте: открыть браузер, найти элемент, кликнуть, проверить результат — и так для каждого сценария. Это работает… до первого редизайна.
Реальность такая: интерфейс меняется. Где-то добавили новый блок, где-то поменяли ID элемента, где-то кнопка переехала вниз. Если у вас сотни автотестов, и каждый напрямую обращается к UI-элементам, вы потратите дни, чтобы это всё поправить вручную.
Поэтому мы сразу внедрили паттерн проектирования Page Object. Суть простая: выносите описание элементов и взаимодействие с ними в отдельные классы. Один класс — один экран или компонент. Это помогает централизовать логику взаимодействия с UI: при изменении интерфейса нужно обновить только один файл, а не всю пачку тестов.
Пример. У нас есть интерфейс с несколькими виджетами: погода, такси, новости. Для каждого виджета нужно создать свой Page-класс в IntelliJ IDEA, чтобы было проще писать автотесты. Короче говоря, паттерн нужен для того, чтобы визуально вам было проще писать и поддерживать автотесты.
Итак, мы создали, например, Page, который назвали PageTaxi. В этом классе мы будем описывать элементы и методы к ним — и всё, больше ничего. Вся остальная логика будет перемещена в тестовый класс. Нам его также нужно создать и назовем его TestTaxi. В этом классе будут писаться сами тесты, которые в дальнейшем будут запускаться.
Ниже приведен пример одного из автотестов:

А вот пример Page-класса: сначала мы описываем элемент, с которым будем работать, а потом метод для этого элемента. Вот как это выглядит на практике:

Когда разработчики меняют логику или структуру страницы — вы правите один файл, а не 47 тестов вручную. Особенно это важно для начинающих автоматизаторов, которым ещё сложно держать в голове масштаб и связи между сценариями.
Если вы планируете развивать проект, а не переписывать его каждый месяц — без Page Object дальше будет больно.
Как мы боремся с нестабильными UI-тестами (и что такое flaky)
На этом этапе уже понятно, как начать писать автотесты. Про настройку проекта в вашей IDE я здесь расписывать не буду — в интернете хватает гайдлайнов, и каждый всё равно настраивает под себя.
Дальше интереснее — как мы в TData поддерживаем автотесты в живом состоянии, особенно когда они внезапно начинают сыпаться. Автотесты могут начать резко падать — иногда по вашей вине, иногда по вине разработчиков, а иногда по независимым от всех обстоятельствам. В целом, UI автотесты считаются самыми нестабильными из всех. Если вам знакомо слово flaky, то вы знаете, о чём речь: тест, который один раз падает, а потом вдруг проходит без изменений. Такие тесты опасны — они подрывают доверие ко всей автоматизации. Поэтому в нашей практике любой flaky — это сигнал, что нужно пересмотреть его логику и устойчивость.
Если у вас есть автотест, который постоянно проходит успешно со второго или третьего раза, и вы не понимаете, в чем дело, то вы можете добавить соответствующую логику в ваш проект. Например, вы можете внести изменения в файл build.gradle, чтобы улучшить стабильность тестов.

Благодаря этой логике упавший тест самостоятельно запустится еще три раза, пока не будет успешно пройден.
Как мы работаем с данными в автотестах
В автоматизированном тестировании качество данных — половина успеха. Если автотесты работают на «сырых» или устаревших данных, никакая крутая инфраструктура не спасёт. В TData мы уделяем этому особое внимание: данные, используемые в автотестах, должны быть не просто актуальными, но и строго соответствовать сценариям, описанным в тест-кейсах.
Откуда берутся эти данные и как мы их готовим?
Все начинается с тест-кейсов, которые создаются вручную тестировщиками и разработчиками. В них описаны конкретные шаги, условия и ожидаемые результаты для различных пользовательских сценариев. Рассмотрим подробнее, как происходит этот процесс:
Тестировщики — изучают функциональные требования, анализируют бизнес-логику использования продукта и прописывают, какие данные понадобятся для проверки того или иного поведения. Они также учитывают различные условия, при которых приложение должно работать корректно — то есть рассматривают не только «счастливые сценарии», но нестандартные случаи.
Разработчики — участвуют, когда вносят изменения в продукт: добавляют новые поля формы, меняют API, исправляют баги. Они подсказывают, какие данные нужно включить в автотесты, чтобы изменения покрывались максимально полно.
Так мы собираем актуальную и живую базу тестовых данных, которая не только отражает реальное поведение системы, но и адаптируется вместе с её развитием.
Интеграция Selenoid в CI: как мы запускаем UI-тесты в TData
Для автоматизации UI-тестирования мы используем Selenoid — инструмент для параллельного выполнения тестов на различных браузерах и платформах. Это позволяет нам эффективно проверять функциональность и производительность приложения, обеспечивая быструю обратную связь для разработчиков. Selenoid интегрирован в наш CI/CD-процесс через Jenkins: при каждом коммите запускается джоб, который параллельно прогоняет набор UI-тестов. Это позволяет покрывать кросс-браузерные сценарии и заметно сокращает общее время выполнения. Плюс — можно гибко масштабироваться при росте нагрузки.
Каждый тест запускается в изолированном контейнере с нужным браузером. Поддерживается VNC-доступ, запись видео и сбор логов — всё это критично, когда нужно разобраться, почему тест упал. После завершения теста контейнер удаляется, освобождая ресурсы. В результате мы получили стабильную и масштабируемую инфраструктуру UI-тестирования, удобную в сопровождении и прозрачную при анализе ошибок. Ниже — как выглядит один из автотестов в интерфейсе Selenoid:

Как писать API автотесты
После успешного освоения UI автотестов, пришло время перейти к автоматизации API тестов. Важно понимать, что API (Application Programming Interface) — это интерфейс, который позволяет различным приложениям взаимодействовать друг с другом. В отличие от фронтенда, здесь нет визуального слоя, и всё взаимодействие с системой происходит через запросы и ответы. По сути, API — это публичный контракт между сервисами. Его можно сравнить с меню в ресторане: вы видите, какие данные и функции доступны, и как их можно запросить.
Шаг 1: Изучение документации API
Первое, с чего начинается автоматизация API тестов, — это тщательное изучение документации. В TData мы используем Swagger — инструмент, позволяющий удобно и интерактивно работать с API спецификацией. В Swagger вы найдете все доступные эндпоинты, параметры, типы ответов и коды состояний. Это упрощает навигацию по контракту API и даёт возможность сразу проверить вручную структуру запросов и ответов, прежде чем писать автотест.
Один из базовых примеров — метод получения списка пользователей. Он задокументирован в Swagger и покрывается автотестом, проверяющим, что возвращаемые данные корректны по структуре, статусу и содержанию.
Шаг 2: Тесты на Rest Assured
В качестве основного инструмента для написания API тестов мы используем библиотеку Rest Assured. Она предоставляет декларативный и лаконичный DSL для отправки HTTP-запросов и проверки ответов. Тест строится по цепочке: формируем запрос с нужными хедерами и параметрами, отправляем его, проверяем статус, валидируем структуру ответа и извлекаем нужные данные. Всё это читается как скрипт:

Шаг 3: Использование POJO-классов
В отличие от UI-автотестов, где применяется паттерн Page Object, API автотесты часто пишутся в одном классе. Однако для улучшения читаемости и поддержки кода рекомендуется использовать POJO (Plain Old Java Object) классы. Они помогают структурировать данные и делают код более понятным.
Вот пример POJO класса для пользователя:

При помощи чего мы делаем отчеты автотестов в TData
Когда тесты уже написаны и начинают работать на каждом коммите, важно не просто знать, прошли ли они, но и быстро понимать, что именно упало, почему и в каком контексте. Лучше всего с этой задачей справляется Allure — инструмент для генерации понятных и визуально привлекательных отчетов. Allure агрегирует информацию о тестах: статус, время выполнения, шаги, логи, вложения, и визуализирует всё это в удобной форме. С его помощью легко отследить прогресс по тестам, увидеть повторяющиеся сбои, оценить покрытие сценариев или быстро найти причину падения. Это особенно полезно как для QA-инженеров, так и для разработчиков — каждый может быстро найти нужную информацию без копания в логах CI.
Ниже — пример отчёта Allure после прогона 600 тестов с распределением по функциональным блокам:

Кастомизация Allure отчетов с помощью аннотаций
Если вы обратили внимание на скриншоты выше, могли заметить, что над тестами в коде используются аннотации, например @DisplayName. Почти все аннотации у нас в проекте используются для кастомизации Allure отчета, и это тоже не исключение. Конкретно @DisplayName позволяет задать понятное и читаемое название для теста, которое будет отображаться в отчёте вместо имени метода. Вместо shouldReturn200WhenUserExists () вы видите, например: «Получение пользователя по ID — 200 OK при валидном токене». Такая детализация значительно повышает читаемость отчёта и помогает быстрее понять, что именно проверялось в каждом тесте. Мы стараемся описывать тесты так, чтобы даже человек, не писавший этот код, мог по отчёту понять суть сценария и причину возможного падения. Это особенно полезно при анализе регрессий или при разборе отчётов командой.
Собрали свою систему — делимся выводами
Автоматизация тестирования — это не про «нажал кнопку — всё проверилось». Это про инфраструктуру, поддерживаемый код, подход к данным, стабильность и здравый смысл. Мы в TData прошли путь от первых UI-скриптов до комплексной системы с API-тестами, CI-интеграцией, репортами, логированием и защитой от flaky.
Если вы только начинаете — не бойтесь стартовать с простого. Не обязательно сразу строить идеальную пирамиду тестов, выравнивать всё по best practices и настраивать пять окружений. Важно, чтобы автоматизация решала конкретные задачи и давала ценность здесь и сейчас: экономила время, снимала рутину, помогала находить баги раньше.
Надеемся, наш опыт окажется полезным и поможет вам сэкономить время и избежать лишних проб и ошибок. Если останутся вопросы — пишите, будем рады обсудить.