Покрытие регресса автотестами: практический опыт внедрения E2E

24 февраля 2026

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

Немного о проекте

Проект представляет собой распределенную систему, состоящую из двух web-порталов на React, порядка двадцати микросервисов на .NET и нескольких интеграций со сторонними системами. Все компоненты участвуют в одном сквозном бизнес-процессе, а релизы выходят регулярно — в среднем раз в две недели.

QA-инженер подключился к проекту уже после начала активной разработки. В этот момент мы осознанно отказались от наращивания объемной ручной тестовой документации и сделали ставку на E2E-автотесты.

Почему Е2Е?

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

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

Выбор стека

Для написания E2E-тестов был выбран TypeScript. Это решение оказалось принципиальным: тот же язык используется во фронтенде, а значит автотесты стали понятны не только QA, но и разработчикам. Ревью тестов, обсуждение архитектуры и доработка сценариев перестали быть зоной ответственности только тестировщика.

В качестве фреймворка мы остановились на Cypress. Cypress хорошо интегрируется с CI/CD, имеет понятную документацию, активное сообщество и экосистему плагинов и расширений. За счет этого проще решать нестандартные задачи и развивать автотесты по мере роста продукта.

Для хранения тест-кейсов регресса и результатов прогонов мы использовали Team Foundation Server (Azure DevOps) — тот же инструмент, где уже находились код, задачи и документация по проекту. TFS это комбайн со встроенным GIT и TMS. И для данной задачи встроенного функционала возможностей оказалось достаточно для наших целей: кейсы связаны с PBI, имеют статус автоматизации и привязку к конкретным автотестам.

1 (8)

(выбор статуса автоматизации при создании кейса в тест-плане)

Как строилось покрытие регресса

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

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

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

2 (1)

(общий график с результатами прогона тестов в тест плане)

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

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

При тестировании новых задач автоматизация стала частью планирования. Если автотест не удавалось написать в рамках работы над PBI, чтобы не задерживать релиз, кейс фиксировался в тест-плане, а задача на автоматизацию возвращалась в бэклог. Это позволило сохранять баланс между скоростью поставки и качеством.

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

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

Поскольку в автотестах недопустимы явные ожидания (а помимо этого официальные рекомендации Cypress также предлагают избегать явных таймаутов), нам потребовалась помощь backend-разработчиков. Для тестов был реализован специальный метод, который последовательно запускает необходимые job’ы и возвращает результат выполнения.

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

3 (1)

(код автотеста)

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

Оптимизация Cypress и поддерживаемость тестов

Cypress использует собственную модель асинхронности, не поддерживающую async/await, из-за чего при автоматизации легко столкнуться с так называемым callback hell — глубокой вложенностью then() и коллбэков.

С помощью экспертизы фронтенд-разработчиков мы решили эту проблему, реализовав базовый класс с использованием Proxy, который автоматически оборачивает вызовы методов в cy.then().

export class YieldBase {

  constructor() {

    return new Proxy(this, {

      get(target, propKey, receiver) {

        const targetValue = Reflect.get(target, propKey, receiver);

 

        if (typeof targetValue !== 'function') {

          return targetValue;        }

 

        return (...args: unknown[]) =>

          cy.then(targetValue.bind(target, ...args));

      },

    });

  }

}

Все page object’ы наследуются от YieldBase. Это позволяет выстраивать асинхронные операции в линейный и читаемый сценарий без явной вложенности.

Отдельного внимания требуют мутирующие команды, когда асинхронная операция изменяет состояние переданного объекта, а не возвращает значение напрямую, например при создании сущности в базе данных: 

Cypress.Commands.add('createRequirement', (requirement) => {

  databases.core

    .insertInto('qt.Requirement')

    .values(requirement.toDatabaseModel())

    .returning('Id')

    .executeQuery()

    .then(({ rows }) => {

      requirement.model.id = Number(rows[0].Id);

    });

});

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

Результаты автоматизации регресса

Что мы получили за счет такой атвоматизации:

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

На данный момент автоматизировано 145 E2E-сценариев разной сложности. Если в среднем брать около трех минут на ручное прохождение одного кейса, полный регресс занял бы целый рабочий день тестировщика. Прогон автотестов в пайплайне длится около 25 минут. Автотесты используются как предрелизный регресс и находят до 20% всех дефектов на тестовом окружении в регрессе. Согласно World Quality Report, зрелость автоматизации напрямую влияет на снижение количества дефектов, обнаруживаемых после релиза.

4 (1)

(диаграмма багов, обнаруженных на тестовом окружении в разбивке по способу обнаружения)